Merge pull request #223 from blockworks-foundation/lou/perp-positions-nav

Add unrealized pnl footer to perp positions
This commit is contained in:
saml33 2023-08-10 14:23:19 +10:00 committed by GitHub
commit c96c7dd5ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 181 additions and 14 deletions

View File

@ -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"

View File

@ -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}

View File

@ -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 ? (

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -71,6 +71,7 @@
"quote": "计价",
"reduce": "Reduce",
"reduce-only": "限减少",
"return-on-equity": "Return on Equity",
"sells": "卖单",
"settle-funds": "借清资金",
"settle-funds-error": "借清出错",

View File

@ -72,6 +72,7 @@
"realized-pnl": "已實現的盈虧",
"reduce": "Reduce",
"reduce-only": "限減少",
"return-on-equity": "Return on Equity",
"sells": "賣單",
"settle-funds": "借清資金",
"settle-funds-error": "借清出錯",