Merge pull request #223 from blockworks-foundation/lou/perp-positions-nav
Add unrealized pnl footer to perp positions
This commit is contained in:
commit
c96c7dd5ed
|
@ -1,16 +1,19 @@
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { formatCurrencyValue } from 'utils/numbers'
|
||||
import FormatNumericValue from './FormatNumericValue'
|
||||
|
||||
const PnlTooltipContent = ({
|
||||
unrealizedPnl,
|
||||
realizedPnl,
|
||||
totalPnl,
|
||||
unsettledPnl,
|
||||
roe,
|
||||
}: {
|
||||
unrealizedPnl: number
|
||||
realizedPnl: number
|
||||
totalPnl: number
|
||||
unsettledPnl: number
|
||||
roe: number
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
return (
|
||||
|
@ -42,6 +45,13 @@ const PnlTooltipContent = ({
|
|||
{formatCurrencyValue(totalPnl, 2)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<p className="mr-3">{t('trade:return-on-equity')}</p>
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue classNames="text-xs" value={roe} decimals={2} />
|
||||
%{' '}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href="https://docs.mango.markets/mango-markets/settle-pnl"
|
||||
|
|
|
@ -125,6 +125,7 @@ const PerpPositionsStatsTable = ({
|
|||
realizedPnl={realizedPnl}
|
||||
totalPnl={totalPnl}
|
||||
unsettledPnl={unsettledPnl}
|
||||
roe={roe}
|
||||
/>
|
||||
}
|
||||
delay={100}
|
||||
|
@ -315,6 +316,7 @@ const PerpPositionsStatsTable = ({
|
|||
realizedPnl={realizedPnl}
|
||||
totalPnl={totalPnl}
|
||||
unsettledPnl={unsettledPnl}
|
||||
roe={roe}
|
||||
/>
|
||||
}
|
||||
delay={100}
|
||||
|
|
|
@ -14,7 +14,7 @@ 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 { useCallback, useMemo, useState } from 'react'
|
||||
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||
|
@ -46,6 +46,42 @@ const PerpPositions = () => {
|
|||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const totalPnlStats = useMemo(() => {
|
||||
if (openPerpPositions.length && group !== undefined) {
|
||||
const pnlByMarket = openPerpPositions.map((position) => {
|
||||
const market = group.getPerpMarketByMarketIndex(position.marketIndex)
|
||||
const basePosition = position.getBasePositionUi(market)
|
||||
const avgEntryPrice = position.getAverageEntryPriceUi(market)
|
||||
return {
|
||||
unrealized: position.getUnRealizedPnlUi(market),
|
||||
realized: position.getRealizedPnlUi(),
|
||||
total: position.cumulativePnlOverPositionLifetimeUi(market),
|
||||
unsettled: position.getUnsettledPnlUi(market),
|
||||
averageEntryValue: Math.abs(basePosition) * avgEntryPrice,
|
||||
}
|
||||
})
|
||||
|
||||
const p = pnlByMarket.reduce((a, b) => {
|
||||
return {
|
||||
unrealized: a.unrealized + b.unrealized,
|
||||
realized: a.realized + b.realized,
|
||||
total: a.total + b.total,
|
||||
unsettled: a.unsettled + b.unsettled,
|
||||
averageEntryValue: a.averageEntryValue + b.averageEntryValue,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
unrealized: p.unrealized,
|
||||
realized: p.realized,
|
||||
total: p.total,
|
||||
unsettled: p.unsettled,
|
||||
roe: (p.unrealized / p.averageEntryValue) * 100,
|
||||
}
|
||||
}
|
||||
return { unrealized: 0, realized: 0, total: 0, unsettled: 0, roe: 0 }
|
||||
}, [openPerpPositions, group])
|
||||
|
||||
const handlePositionClick = (positionSize: number, market: PerpMarket) => {
|
||||
const tradeForm = mangoStore.getState().tradeForm
|
||||
const set = mangoStore.getState().set
|
||||
|
@ -229,6 +265,7 @@ const PerpPositions = () => {
|
|||
realizedPnl={realizedPnl}
|
||||
totalPnl={totalPnl}
|
||||
unsettledPnl={unsettledPnl}
|
||||
roe={roe}
|
||||
/>
|
||||
}
|
||||
delay={100}
|
||||
|
@ -247,19 +284,6 @@ const PerpPositions = () => {
|
|||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span
|
||||
className={roe >= 0 ? 'text-th-up' : 'text-th-down'}
|
||||
>
|
||||
<FormatNumericValue
|
||||
classNames="text-xs"
|
||||
value={roe}
|
||||
decimals={2}
|
||||
/>
|
||||
%{' '}
|
||||
<span className="font-body text-xs text-th-fgd-3">
|
||||
(ROE)
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</Td>
|
||||
{!isUnownedAccount ? (
|
||||
|
@ -288,6 +312,65 @@ const PerpPositions = () => {
|
|||
</TrBody>
|
||||
)
|
||||
})}
|
||||
{openPerpPositions.length > 0 ? (
|
||||
<tr
|
||||
key={`total-unrealized-pnl`}
|
||||
className="my-1 p-2 border-y border-th-bkg-3"
|
||||
>
|
||||
<Td className="text-right font-mono">
|
||||
<></>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<></>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<></>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<></>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<div className="flex justify-end items-center">
|
||||
<span className="font-body mr-4 text-md text-th-fgd-3">
|
||||
Total:
|
||||
</span>
|
||||
<Tooltip
|
||||
content={
|
||||
<PnlTooltipContent
|
||||
unrealizedPnl={totalPnlStats.unrealized}
|
||||
realizedPnl={totalPnlStats.realized}
|
||||
totalPnl={totalPnlStats.total}
|
||||
unsettledPnl={totalPnlStats.unsettled}
|
||||
roe={totalPnlStats.roe}
|
||||
/>
|
||||
}
|
||||
delay={100}
|
||||
>
|
||||
<div className="flex">
|
||||
<span>
|
||||
<FormatNumericValue
|
||||
classNames={`tooltip-underline ${
|
||||
totalPnlStats.unrealized >= 0
|
||||
? 'text-th-up'
|
||||
: 'text-th-down'
|
||||
}`}
|
||||
value={totalPnlStats.unrealized}
|
||||
isUsd
|
||||
decimals={2}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Td>
|
||||
{!isUnownedAccount ? (
|
||||
<Td className="text-right font-mono">
|
||||
{' '}
|
||||
<></>
|
||||
</Td>
|
||||
) : null}
|
||||
</tr>
|
||||
) : null}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
|
@ -493,6 +576,7 @@ const PerpPositions = () => {
|
|||
realizedPnl={realizedPnl}
|
||||
totalPnl={totalPnl}
|
||||
unsettledPnl={unsettledPnl}
|
||||
roe={roe}
|
||||
/>
|
||||
}
|
||||
delay={100}
|
||||
|
@ -552,6 +636,72 @@ const PerpPositions = () => {
|
|||
</Disclosure>
|
||||
)
|
||||
})}
|
||||
{openPerpPositions.length > 0 ? (
|
||||
<>
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`flex w-full justify-end border-t border-th-bkg-3 p-1 text-right focus:outline-none`}
|
||||
>
|
||||
<div className="flex flex-col justify-end mt-1 ml-auto">
|
||||
<div className="flex flex-row">
|
||||
<span className="font-body mr-3 text-md text-th-fgd-3">
|
||||
Total Unrealized PnL:
|
||||
</span>
|
||||
<span
|
||||
className={`font-mono mr-2 ${
|
||||
totalPnlStats.unrealized > 0
|
||||
? 'text-th-up'
|
||||
: 'text-th-down'
|
||||
}`}
|
||||
>
|
||||
<FormatNumericValue
|
||||
value={totalPnlStats.unrealized}
|
||||
isUsd
|
||||
decimals={2}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-end">
|
||||
<Transition
|
||||
enter="transition ease-in duration-200"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
>
|
||||
<Disclosure.Panel className="mt-1">
|
||||
<span className="font-body mr-3 text-md text-right text-th-fgd-3">
|
||||
Total ROE:
|
||||
</span>
|
||||
<span
|
||||
className={`font-mono mr-1.5 ${
|
||||
totalPnlStats.roe >= 0
|
||||
? 'text-th-up'
|
||||
: 'text-th-down'
|
||||
}`}
|
||||
>
|
||||
<FormatNumericValue
|
||||
value={totalPnlStats.roe}
|
||||
decimals={2}
|
||||
/>
|
||||
%{' '}
|
||||
</span>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
open ? 'rotate-180' : 'rotate-360'
|
||||
} mr-3 mt-1 h-6 w-6 flex-shrink-0 text-th-fgd-3`}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
) : mangoAccount || connected ? (
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"realized-pnl": "Realized PnL",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "Reduce Only",
|
||||
"return-on-equity": "Return on Equity",
|
||||
"sells": "Sells",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"realized-pnl": "Realized PnL",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "Reduce Only",
|
||||
"return-on-equity": "Return on Equity",
|
||||
"sells": "Sells",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"realized-pnl": "Realized PnL",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "Reduce Only",
|
||||
"return-on-equity": "Return on Equity",
|
||||
"sells": "Sells",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
"quote": "计价",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "限减少",
|
||||
"return-on-equity": "Return on Equity",
|
||||
"sells": "卖单",
|
||||
"settle-funds": "借清资金",
|
||||
"settle-funds-error": "借清出错",
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"realized-pnl": "已實現的盈虧",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "限減少",
|
||||
"return-on-equity": "Return on Equity",
|
||||
"sells": "賣單",
|
||||
"settle-funds": "借清資金",
|
||||
"settle-funds-error": "借清出錯",
|
||||
|
|
Loading…
Reference in New Issue