diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index b316a3f54..ab64f77b1 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -94,7 +94,11 @@ const MainGrid = memo( > - + } + > ( diff --git a/apps/trading/components/positions-container/positions-container.tsx b/apps/trading/components/positions-container/positions-container.tsx index 518f7c2d6..f830d4666 100644 --- a/apps/trading/components/positions-container/positions-container.tsx +++ b/apps/trading/components/positions-container/positions-container.tsx @@ -5,6 +5,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit'; import { useVegaWallet } from '@vegaprotocol/wallet'; import type { DataGridSlice } from '../../stores/datagrid-store-slice'; import { createDataGridSlice } from '../../stores/datagrid-store-slice'; +import type { StateCreator } from 'zustand'; import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; @@ -13,6 +14,7 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => { const onMarketClick = useMarketClickHandler(true); const { pubKey, pubKeys, isReadOnly } = useVegaWallet(); + const showClosed = usePositionsStore((store) => store.showClosedMarkets); const gridStore = usePositionsStore((store) => store.gridStore); const updateGridStore = usePositionsStore((store) => store.updateGridStore); const gridStoreCallbacks = useDataGridEvents(gridStore, updateGridStore); @@ -40,12 +42,35 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => { onMarketClick={onMarketClick} isReadOnly={isReadOnly} gridProps={gridStoreCallbacks} + showClosed={showClosed} /> ); }; -const usePositionsStore = create()( - persist(createDataGridSlice, { - name: 'vega_positions_store', - }) +type PositionsStoreSlice = { + showClosedMarkets: boolean; + toggleClosedMarkets: () => void; +}; + +const createPositionStoreSlice: StateCreator = (set) => ({ + showClosedMarkets: false, + toggleClosedMarkets: () => { + set((curr) => { + return { + showClosedMarkets: !curr.showClosedMarkets, + }; + }); + }, +}); + +export const usePositionsStore = create()( + persist( + (...args) => ({ + ...createPositionStoreSlice(...args), + ...createDataGridSlice(...args), + }), + { + name: 'vega_positions_store', + } + ) ); diff --git a/apps/trading/components/positions-menu/index.ts b/apps/trading/components/positions-menu/index.ts new file mode 100644 index 000000000..12fc9ea53 --- /dev/null +++ b/apps/trading/components/positions-menu/index.ts @@ -0,0 +1 @@ +export * from './positions-menu'; diff --git a/apps/trading/components/positions-menu/positions-menu.tsx b/apps/trading/components/positions-menu/positions-menu.tsx new file mode 100644 index 000000000..58dddee7b --- /dev/null +++ b/apps/trading/components/positions-menu/positions-menu.tsx @@ -0,0 +1,18 @@ +import { t } from '@vegaprotocol/i18n'; +import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit'; +import { usePositionsStore } from '../positions-container'; + +export const PositionsMenu = () => { + const showClosed = usePositionsStore((store) => store.showClosedMarkets); + const toggle = usePositionsStore((store) => store.toggleClosedMarkets); + return ( + + {showClosed ? t('Hide closed markets') : t('Show closed markets')} + + ); +}; diff --git a/libs/positions/src/lib/positions-data-providers.spec.ts b/libs/positions/src/lib/positions-data-providers.spec.ts index a8ca0aaa9..6c1f67093 100644 --- a/libs/positions/src/lib/positions-data-providers.spec.ts +++ b/libs/positions/src/lib/positions-data-providers.spec.ts @@ -2,7 +2,12 @@ import * as Schema from '@vegaprotocol/types'; import type { Account } from '@vegaprotocol/accounts'; import type { MarketWithData } from '@vegaprotocol/markets'; import type { PositionFieldsFragment } from './__generated__/Positions'; -import { getMetrics, rejoinPositionData } from './positions-data-providers'; +import type { Position } from './positions-data-providers'; +import { + getMetrics, + preparePositions, + rejoinPositionData, +} from './positions-data-providers'; import { PositionStatus } from '@vegaprotocol/types'; const accounts = [ @@ -223,4 +228,29 @@ describe('getMetrics && rejoinPositionData', () => { ); expect(metrics[1].status).toEqual(positions[1].positionStatus); }); + + it('sorts and filters positions', () => { + const createPosition = (override?: Partial) => + ({ + marketState: Schema.MarketState.STATE_ACTIVE, + marketCode: 'a', + ...override, + } as Position); + + const data = [ + createPosition(), + createPosition({ + marketCode: 'c', + marketState: Schema.MarketState.STATE_CANCELLED, + }), + createPosition({ marketCode: 'd' }), + createPosition({ marketCode: 'b' }), + ]; + + const withoutClosed = preparePositions(data, false); + expect(withoutClosed.map((p) => p.marketCode)).toEqual(['a', 'b', 'd']); + + const withClosed = preparePositions(data, true); + expect(withClosed.map((p) => p.marketCode)).toEqual(['a', 'b', 'c', 'd']); + }); }); diff --git a/libs/positions/src/lib/positions-data-providers.ts b/libs/positions/src/lib/positions-data-providers.ts index 8d44c0941..d5c533b83 100644 --- a/libs/positions/src/lib/positions-data-providers.ts +++ b/libs/positions/src/lib/positions-data-providers.ts @@ -41,6 +41,7 @@ export interface Position { marketId: string; marketCode: string; marketTradingMode: Schema.MarketTradingMode; + marketState: Schema.MarketState; markPrice: string | undefined; notional: string | undefined; openVolume: string; @@ -119,6 +120,7 @@ export const getMetrics = ( marketId: market.id, marketCode: market.tradableInstrument.instrument.code, marketTradingMode: market.tradingMode, + marketState: market.state, markPrice: marketData ? marketData.markPrice : undefined, notional: notional ? notional.multipliedBy(10 ** marketDecimalPlaces).toFixed(0) @@ -248,6 +250,26 @@ export const rejoinPositionData = ( return null; }; +export const preparePositions = (metrics: Position[], showClosed: boolean) => { + return sortBy(metrics, 'marketCode').filter((p) => { + if (showClosed) { + return true; + } + + if ( + [ + Schema.MarketState.STATE_ACTIVE, + Schema.MarketState.STATE_PENDING, + Schema.MarketState.STATE_SUSPENDED, + ].includes(p.marketState) + ) { + return true; + } + + return false; + }); +}; + export const positionsMarketsProvider = makeDerivedDataProvider< string[], never, @@ -265,7 +287,7 @@ export const positionsMarketsProvider = makeDerivedDataProvider< export const positionsMetricsProvider = makeDerivedDataProvider< Position[], Position[], - PositionsQueryVariables & { marketIds: string[] } + PositionsQueryVariables & { marketIds: string[]; showClosed: boolean } >( [ (callback, client, variables) => @@ -281,10 +303,10 @@ export const positionsMetricsProvider = makeDerivedDataProvider< marketIds: variables.marketIds, }), ], - ([positions, accounts, marketsData]) => { + ([positions, accounts, marketsData], variables) => { const positionsData = rejoinPositionData(positions, marketsData); const metrics = getMetrics(positionsData, accounts as Account[] | null); - return sortBy(metrics, 'marketCode'); + return preparePositions(metrics, variables.showClosed); }, (data, delta, previousData) => data.filter((row) => { diff --git a/libs/positions/src/lib/positions-manager.tsx b/libs/positions/src/lib/positions-manager.tsx index f01f71cfa..521ae1b45 100644 --- a/libs/positions/src/lib/positions-manager.tsx +++ b/libs/positions/src/lib/positions-manager.tsx @@ -16,6 +16,7 @@ interface PositionsManagerProps { onMarketClick?: (marketId: string) => void; isReadOnly: boolean; gridProps?: ReturnType; + showClosed?: boolean; } export const PositionsManager = ({ @@ -23,6 +24,7 @@ export const PositionsManager = ({ onMarketClick, isReadOnly, gridProps, + showClosed = false, }: PositionsManagerProps) => { const { pubKeys, pubKey } = useVegaWallet(); const create = useVegaTransactionStore((store) => store.create); @@ -60,7 +62,7 @@ export const PositionsManager = ({ const { data, error } = useDataProvider({ dataProvider: positionsMetricsProvider, - variables: { partyIds, marketIds: marketIds || [] }, + variables: { partyIds, marketIds: marketIds || [], showClosed }, skip: !marketIds, }); @@ -68,7 +70,7 @@ export const PositionsManager = ({ ); }, - minWidth: 75, - maxWidth: 75, + minWidth: 55, + maxWidth: 55, } : null, ]; diff --git a/specs/7004-POSI-positions.md b/specs/7004-POSI-positions.md index 3bcb69493..5965f24f8 100644 --- a/specs/7004-POSI-positions.md +++ b/specs/7004-POSI-positions.md @@ -51,4 +51,8 @@ - **Must** be able to see if your realised PnL was affected by loss socialisation (7004-POSI-018) -- **Must** Must be able to see what type of product the position was opened on (7004-POSI-019) +- **Must** be able to see what type of product the position was opened on (7004-POSI-019) + +- **Must** not see positions on markets which are closed (7004-POSI-020) + +- **Must** be able to show closed markets (7004-POSI-021)