import { useCallback, useState } from 'react' import useMangoStore from '../stores/useMangoStore' import { getMarketByPublicKey, PerpMarket, ZERO_BN, } from '@blockworks-foundation/mango-client' import SideBadge from './SideBadge' import { useViewport } from '../hooks/useViewport' import { breakpoints } from './TradePageGrid' import { Table, Td, Th, TrBody, TrHead } from './TableElements' import { formatUsdValue, usdFormatter } from '../utils' import useTradeHistory from '../hooks/useTradeHistory' import usePerpPositions from '../hooks/usePerpPositions' import MarketCloseModal from './MarketCloseModal' import { ExpandableRow } from './TableElements' import MobileTableHeader from './mobile/MobileTableHeader' export function getAvgEntryPrice( mangoAccount, perpAccount, perpMarket, perpTradeHistory ) { let avgEntryPrice = '--' if (perpTradeHistory.length) { try { avgEntryPrice = formatUsdValue( perpAccount.getAverageOpenPrice( mangoAccount, perpMarket, perpTradeHistory ) ) } catch { avgEntryPrice = '--' } } return avgEntryPrice } export function getBreakEvenPrice( mangoAccount, perpAccount, perpMarket, perpTradeHistory ) { let breakEvenPrice = '--' if (perpTradeHistory.length) { try { breakEvenPrice = formatUsdValue( perpAccount.getBreakEvenPrice( mangoAccount, perpMarket, perpTradeHistory ) ) } catch { breakEvenPrice = '--' } } return breakEvenPrice } const PositionsTable = () => { const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config) const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current) const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache) const allMarkets = useMangoStore((s) => s.selectedMangoGroup.markets) const [showMarketCloseModal, setShowMarketCloseModal] = useState(false) const tradeHistory = useTradeHistory() const setMangoStore = useMangoStore((s) => s.set) const perpPositions = usePerpPositions() const { width } = useViewport() const isMobile = width ? width < breakpoints.md : false const handleCloseWarning = useCallback(() => { setShowMarketCloseModal(false) }, []) const handleSizeClick = (size) => { setMangoStore((state) => { state.tradeForm.baseSize = size }) } return (
{perpPositions.length ? ( !isMobile ? ( {perpPositions.map(({ perpAccount, marketIndex }, index) => { const perpMarketInfo = mangoGroup.perpMarkets[marketIndex] const marketConfig = getMarketByPublicKey( groupConfig, perpMarketInfo.perpMarket ) const perpMarket = allMarkets[ perpMarketInfo.perpMarket.toString() ] as PerpMarket const perpTradeHistory = tradeHistory.filter( (t) => t.marketName === marketConfig.name ) let breakEvenPrice try { breakEvenPrice = perpAccount.getBreakEvenPrice( mangoAccount, perpMarket, perpTradeHistory ) } catch (e) { breakEvenPrice = null } const pnl = breakEvenPrice !== null ? perpMarket.baseLotsToNumber( perpAccount.basePosition ) * (mangoGroup .getPrice(marketIndex, mangoCache) .toNumber() - parseFloat(breakEvenPrice)) : null return ( {showMarketCloseModal ? ( ) : null} ) })}
Market Side Position Size Notional Size Avg entry Price Break-even Price PnL
{marketConfig.name}
{!perpAccount.basePosition.eq(ZERO_BN) ? ( ) : ( '-' )} {perpAccount && Math.abs( perpMarket.baseLotsToNumber( perpAccount.basePosition ) ) > 0 ? ( handleSizeClick( Math.abs( perpMarket.baseLotsToNumber( perpAccount.basePosition ) ) ) } > {`${Math.abs( perpMarket.baseLotsToNumber( perpAccount.basePosition ) )} ${marketConfig.baseSymbol}`} ) : ( `0 ${marketConfig.baseSymbol}` )} {usdFormatter( Math.abs( perpMarket.baseLotsToNumber( perpAccount.basePosition ) * mangoGroup .getPrice(marketIndex, mangoCache) .toNumber() ) )} {getAvgEntryPrice( mangoAccount, perpAccount, perpMarket, perpTradeHistory )} {getBreakEvenPrice( mangoAccount, perpAccount, perpMarket, perpTradeHistory )} {pnl !== null ? ( pnl > 0 ? ( {usdFormatter(pnl)} ) : ( {usdFormatter(pnl)} ) ) : ( '--' )}
) : ( <> Position
} /> {perpPositions.map(({ perpAccount, marketIndex }, index) => { const perpMarketInfo = mangoGroup.perpMarkets[marketIndex] const marketConfig = getMarketByPublicKey( groupConfig, perpMarketInfo.perpMarket ) const perpMarket = allMarkets[ perpMarketInfo.perpMarket.toString() ] as PerpMarket const perpTradeHistory = tradeHistory.filter( (t) => t.marketName === marketConfig.name ) let breakEvenPrice try { breakEvenPrice = perpAccount.getBreakEvenPrice( mangoAccount, perpMarket, perpTradeHistory ) } catch (e) { breakEvenPrice = null } const pnl = breakEvenPrice !== null ? perpMarket.baseLotsToNumber(perpAccount.basePosition) * (mangoGroup .getPrice(marketIndex, mangoCache) .toNumber() - parseFloat(breakEvenPrice)) : null return (
{marketConfig.name}
{perpAccount.basePosition.gt(ZERO_BN) ? 'LONG' : 'SHORT'} {`${ Math.abs( perpMarket.baseLotsToNumber( perpAccount.basePosition ) ) > 0 ? Math.abs( perpMarket.baseLotsToNumber( perpAccount.basePosition ) ) : 0 } at ${getAvgEntryPrice( mangoAccount, perpAccount, perpMarket, perpTradeHistory )}`}
} key={`${index}`} index={index} panelTemplate={ <>
Notional Size
{usdFormatter( Math.abs( perpMarket.baseLotsToNumber( perpAccount.basePosition ) * mangoGroup .getPrice(marketIndex, mangoCache) .toNumber() ) )}
Break-even Price
{getBreakEvenPrice( mangoAccount, perpAccount, perpMarket, perpTradeHistory )}
PnL
{pnl !== null ? ( pnl > 0 ? ( {usdFormatter(pnl)} ) : ( {usdFormatter(pnl)} ) ) : ( '--' )}
} /> ) })} ) ) : (
No perp positions
)}
) } export default PositionsTable