mobile perp positions table
This commit is contained in:
parent
6a05e60376
commit
9dbf7c90a0
|
@ -252,21 +252,14 @@ const OpenOrders = () => {
|
|||
let market: PerpMarket | Serum3Market
|
||||
let tickSize: number
|
||||
let minOrderSize: number
|
||||
let quoteSymbol
|
||||
if (o instanceof PerpOrder) {
|
||||
market = group.getPerpMarketByMarketIndex(o.perpMarketIndex)
|
||||
quoteSymbol = group.getFirstBankByTokenIndex(
|
||||
market.settleTokenIndex
|
||||
).name
|
||||
tickSize = market.tickSize
|
||||
minOrderSize = market.minOrderSize
|
||||
} else {
|
||||
market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(marketPk)
|
||||
)
|
||||
quoteSymbol = group.getFirstBankByTokenIndex(
|
||||
market!.quoteTokenIndex
|
||||
).name
|
||||
const serumMarket = group.getSerum3ExternalMarket(
|
||||
market.serumMarketExternal
|
||||
)
|
||||
|
@ -297,10 +290,7 @@ const OpenOrders = () => {
|
|||
{o.price.toLocaleString(undefined, {
|
||||
minimumFractionDigits: getDecimalCount(tickSize),
|
||||
maximumFractionDigits: getDecimalCount(tickSize),
|
||||
})}{' '}
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{quoteSymbol}
|
||||
</span>
|
||||
})}
|
||||
</span>
|
||||
</Td>
|
||||
</>
|
||||
|
@ -329,7 +319,7 @@ const OpenOrders = () => {
|
|||
</Td>
|
||||
</>
|
||||
)}
|
||||
<Td className="w-[16.67%] text-right">
|
||||
<Td className="w-[16.67%] text-right font-mono">
|
||||
<FormatNumericValue
|
||||
value={o.size * o.price}
|
||||
decimals={2}
|
||||
|
@ -403,14 +393,10 @@ const OpenOrders = () => {
|
|||
let market: PerpMarket | Serum3Market
|
||||
let tickSize: number
|
||||
let minOrderSize: number
|
||||
let quoteSymbol: string
|
||||
let baseSymbol: string
|
||||
if (o instanceof PerpOrder) {
|
||||
market = group.getPerpMarketByMarketIndex(o.perpMarketIndex)
|
||||
baseSymbol = market.name.split('-')[0]
|
||||
quoteSymbol = group.getFirstBankByTokenIndex(
|
||||
market.settleTokenIndex
|
||||
).name
|
||||
tickSize = market.tickSize
|
||||
minOrderSize = market.minOrderSize
|
||||
} else {
|
||||
|
@ -418,9 +404,6 @@ const OpenOrders = () => {
|
|||
new PublicKey(marketPk)
|
||||
)
|
||||
baseSymbol = market.name.split('/')[0]
|
||||
quoteSymbol = group.getFirstBankByTokenIndex(
|
||||
market!.quoteTokenIndex
|
||||
).name
|
||||
const serumMarket = group.getSerum3ExternalMarket(
|
||||
market.serumMarketExternal
|
||||
)
|
||||
|
@ -445,14 +428,13 @@ const OpenOrders = () => {
|
|||
})}
|
||||
</span>{' '}
|
||||
{baseSymbol}
|
||||
{' for '}
|
||||
{' at '}
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
{o.price.toLocaleString(undefined, {
|
||||
minimumFractionDigits: getDecimalCount(tickSize),
|
||||
maximumFractionDigits: getDecimalCount(tickSize),
|
||||
})}
|
||||
</span>{' '}
|
||||
{quoteSymbol}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
@ -10,9 +10,11 @@ import useMangoAccount from 'hooks/useMangoAccount'
|
|||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||
import MarketCloseModal from './MarketCloseModal'
|
||||
import PerpSideBadge from './PerpSideBadge'
|
||||
|
@ -30,6 +32,8 @@ const PerpPositions = () => {
|
|||
const { connected } = useWallet()
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const isUnownedAccount = useUnownedAccount()
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const handlePositionClick = (positionSize: number, market: PerpMarket) => {
|
||||
const tradeForm = mangoStore.getState().tradeForm
|
||||
|
@ -74,53 +78,152 @@ const PerpPositions = () => {
|
|||
)
|
||||
|
||||
return mangoAccountAddress && openPerpPositions.length ? (
|
||||
<>
|
||||
<div className="thin-scroll overflow-x-auto">
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th className="text-left">{t('market')}</Th>
|
||||
<Th className="text-right">{t('trade:side')}</Th>
|
||||
<Th className="text-right">{t('trade:size')}</Th>
|
||||
<Th className="text-right">{t('trade:notional')}</Th>
|
||||
<Th className="text-right">{t('trade:entry-price')}</Th>
|
||||
<Th className="text-right">{`${t('trade:unsettled')} ${t(
|
||||
'pnl'
|
||||
)}`}</Th>
|
||||
<Th className="text-right">{t('pnl')}</Th>
|
||||
{!isUnownedAccount ? <Th /> : null}
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{openPerpPositions.map((position) => {
|
||||
const market = group.getPerpMarketByMarketIndex(
|
||||
position.marketIndex
|
||||
)
|
||||
const basePosition = position.getBasePositionUi(market)
|
||||
const floorBasePosition = floorToDecimal(
|
||||
basePosition,
|
||||
getDecimalCount(market.minOrderSize)
|
||||
).toNumber()
|
||||
const isSelectedMarket =
|
||||
selectedMarket instanceof PerpMarket &&
|
||||
selectedMarket.perpMarketIndex === position.marketIndex
|
||||
showTableView ? (
|
||||
<>
|
||||
<div className="thin-scroll overflow-x-auto">
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th className="text-left">{t('market')}</Th>
|
||||
<Th className="text-right">{t('trade:side')}</Th>
|
||||
<Th className="text-right">{t('trade:size')}</Th>
|
||||
<Th className="text-right">{t('trade:notional')}</Th>
|
||||
<Th className="text-right">{t('trade:entry-price')}</Th>
|
||||
<Th className="text-right">{`${t('trade:unsettled')} ${t(
|
||||
'pnl'
|
||||
)}`}</Th>
|
||||
<Th className="text-right">{t('pnl')}</Th>
|
||||
{!isUnownedAccount ? <Th /> : null}
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{openPerpPositions.map((position) => {
|
||||
const market = group.getPerpMarketByMarketIndex(
|
||||
position.marketIndex
|
||||
)
|
||||
const basePosition = position.getBasePositionUi(market)
|
||||
const floorBasePosition = floorToDecimal(
|
||||
basePosition,
|
||||
getDecimalCount(market.minOrderSize)
|
||||
).toNumber()
|
||||
const isSelectedMarket =
|
||||
selectedMarket instanceof PerpMarket &&
|
||||
selectedMarket.perpMarketIndex === position.marketIndex
|
||||
|
||||
if (!basePosition) return null
|
||||
if (!basePosition) return null
|
||||
|
||||
const unsettledPnl = position.getUnsettledPnlUi(market)
|
||||
const cummulativePnl =
|
||||
position.cumulativePnlOverPositionLifetimeUi(market)
|
||||
const unsettledPnl = position.getUnsettledPnlUi(market)
|
||||
const cummulativePnl =
|
||||
position.cumulativePnlOverPositionLifetimeUi(market)
|
||||
|
||||
return (
|
||||
<TrBody key={`${position.marketIndex}`} className="my-1 p-2">
|
||||
<Td>
|
||||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<PerpSideBadge basePosition={basePosition} />
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<p className="flex justify-end">
|
||||
return (
|
||||
<TrBody key={`${position.marketIndex}`} className="my-1 p-2">
|
||||
<Td>
|
||||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<PerpSideBadge basePosition={basePosition} />
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<p className="flex justify-end">
|
||||
{isSelectedMarket ? (
|
||||
<LinkButton
|
||||
onClick={() =>
|
||||
handlePositionClick(floorBasePosition, market)
|
||||
}
|
||||
>
|
||||
<FormatNumericValue
|
||||
value={Math.abs(basePosition)}
|
||||
decimals={getDecimalCount(market.minOrderSize)}
|
||||
/>
|
||||
</LinkButton>
|
||||
) : (
|
||||
<FormatNumericValue
|
||||
value={Math.abs(basePosition)}
|
||||
decimals={getDecimalCount(market.minOrderSize)}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<FormatNumericValue
|
||||
value={Math.abs(floorBasePosition) * market._uiPrice}
|
||||
isUsd
|
||||
/>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<FormatNumericValue
|
||||
value={position.getAverageEntryPriceUi(market)}
|
||||
decimals={getDecimalCount(market.tickSize)}
|
||||
isUsd
|
||||
/>
|
||||
</Td>
|
||||
<Td className={`text-right font-mono`}>
|
||||
<FormatNumericValue
|
||||
value={unsettledPnl}
|
||||
decimals={market.baseDecimals}
|
||||
/>
|
||||
</Td>
|
||||
<Td
|
||||
className={`text-right font-mono ${
|
||||
cummulativePnl > 0 ? 'text-th-up' : 'text-th-down'
|
||||
}`}
|
||||
>
|
||||
<FormatNumericValue value={cummulativePnl} isUsd />
|
||||
</Td>
|
||||
{!isUnownedAccount ? (
|
||||
<Td className={`text-right`}>
|
||||
<Button
|
||||
className="text-xs"
|
||||
secondary
|
||||
size="small"
|
||||
onClick={() => showClosePositionModal(position)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</Td>
|
||||
) : null}
|
||||
</TrBody>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
{showMarketCloseModal && positionToClose ? (
|
||||
<MarketCloseModal
|
||||
isOpen={showMarketCloseModal}
|
||||
onClose={hideClosePositionModal}
|
||||
position={positionToClose}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{openPerpPositions.map((position) => {
|
||||
const market = group.getPerpMarketByMarketIndex(position.marketIndex)
|
||||
const basePosition = position.getBasePositionUi(market)
|
||||
const floorBasePosition = floorToDecimal(
|
||||
basePosition,
|
||||
getDecimalCount(market.minOrderSize)
|
||||
).toNumber()
|
||||
const isSelectedMarket =
|
||||
selectedMarket instanceof PerpMarket &&
|
||||
selectedMarket.perpMarketIndex === position.marketIndex
|
||||
|
||||
if (!basePosition) return null
|
||||
const cummulativePnl =
|
||||
position.cumulativePnlOverPositionLifetimeUi(market)
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
key={`${position.marketIndex}`}
|
||||
>
|
||||
<div>
|
||||
<TableMarketName market={market} />
|
||||
<div className="mt-1 flex items-center space-x-1">
|
||||
<PerpSideBadge basePosition={basePosition} />
|
||||
<p className="text-th-fgd-4">
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
{isSelectedMarket ? (
|
||||
<LinkButton
|
||||
onClick={() =>
|
||||
|
@ -138,60 +241,42 @@ const PerpPositions = () => {
|
|||
decimals={getDecimalCount(market.minOrderSize)}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<FormatNumericValue
|
||||
value={Math.abs(floorBasePosition) * market._uiPrice}
|
||||
isUsd
|
||||
/>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<FormatNumericValue
|
||||
value={position.getAverageEntryPriceUi(market)}
|
||||
decimals={getDecimalCount(market.tickSize)}
|
||||
isUsd
|
||||
/>
|
||||
</Td>
|
||||
<Td className={`text-right font-mono`}>
|
||||
<FormatNumericValue
|
||||
value={unsettledPnl}
|
||||
decimals={market.baseDecimals}
|
||||
/>
|
||||
</Td>
|
||||
<Td
|
||||
className={`text-right font-mono ${
|
||||
cummulativePnl > 0 ? 'text-th-up' : 'text-th-down'
|
||||
}`}
|
||||
</span>
|
||||
{' at '}
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
<FormatNumericValue
|
||||
value={position.getAverageEntryPriceUi(market)}
|
||||
decimals={getDecimalCount(market.tickSize)}
|
||||
isUsd
|
||||
/>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div
|
||||
className={`text-right font-mono ${
|
||||
cummulativePnl > 0 ? 'text-th-up' : 'text-th-down'
|
||||
}`}
|
||||
>
|
||||
<FormatNumericValue value={cummulativePnl} isUsd />
|
||||
</div>
|
||||
{!isUnownedAccount ? (
|
||||
<Button
|
||||
className="text-xs"
|
||||
secondary
|
||||
size="small"
|
||||
onClick={() => showClosePositionModal(position)}
|
||||
>
|
||||
<FormatNumericValue value={cummulativePnl} isUsd />
|
||||
</Td>
|
||||
{!isUnownedAccount ? (
|
||||
<Td className={`text-right`}>
|
||||
<Button
|
||||
className="text-xs"
|
||||
secondary
|
||||
size="small"
|
||||
onClick={() => showClosePositionModal(position)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</Td>
|
||||
) : null}
|
||||
</TrBody>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
{showMarketCloseModal && positionToClose ? (
|
||||
<MarketCloseModal
|
||||
isOpen={showMarketCloseModal}
|
||||
onClose={hideClosePositionModal}
|
||||
position={positionToClose}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
Close
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
) : mangoAccountAddress || connected ? (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
|
|
|
@ -340,7 +340,7 @@ const TradeHistory = () => {
|
|||
<span className="font-mono text-th-fgd-2">
|
||||
{trade.size}
|
||||
</span>
|
||||
{' for '}
|
||||
{' at '}
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue value={trade.price} />
|
||||
</span>
|
||||
|
|
Loading…
Reference in New Issue