From 95bc34eb72d7d01662935ea124bfbe347b097052 Mon Sep 17 00:00:00 2001 From: Lou-Kamades Date: Fri, 4 Aug 2023 02:20:58 -0500 Subject: [PATCH 1/3] add unrealized pnl to perp positions --- components/trade/PerpPositions.tsx | 73 +++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/components/trade/PerpPositions.tsx b/components/trade/PerpPositions.tsx index e0e89f62..fe593087 100644 --- a/components/trade/PerpPositions.tsx +++ b/components/trade/PerpPositions.tsx @@ -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,18 @@ const PerpPositions = () => { const { width } = useViewport() const showTableView = width ? width > breakpoints.md : false + const totalUnrealizedPnl = useMemo(() => { + if (openPerpPositions.length && group !== undefined) { + return openPerpPositions + .map((position) => { + const market = group.getPerpMarketByMarketIndex(position.marketIndex) + return position.getUnRealizedPnlUi(market) + }) + .reduce((a, b) => a + b) + } + return 0.0 + }, [openPerpPositions, group]) + const handlePositionClick = (positionSize: number, market: PerpMarket) => { const tradeForm = mangoStore.getState().tradeForm const set = mangoStore.getState().set @@ -288,6 +300,41 @@ const PerpPositions = () => { ) })} + {openPerpPositions.length > 0 ? ( + + + <> + + + <> + + + <> + + + <> + + +
+ + Total Unrealized PnL: + + + = 0 + ? 'text-th-up' + : 'text-th-down' + }`} + value={totalUnrealizedPnl} + isUsd + decimals={2} + /> + +
+ +
+ ) : null} @@ -552,6 +599,30 @@ const PerpPositions = () => { ) })} + {openPerpPositions.length > 0 ? ( +
+
+
+ + Total Unrealized PnL: + + 0 ? 'text-th-up' : 'text-th-down' + }`} + > + + +
+
+
+ ) : null}
) ) : mangoAccount || connected ? ( From a67af5bf3239bce5c0908dcbc58d88ff8d60e899 Mon Sep 17 00:00:00 2001 From: Lou-Kamades Date: Wed, 9 Aug 2023 13:49:32 -0500 Subject: [PATCH 2/3] add ROE and styling to PerpPositions footer --- components/trade/PerpPositions.tsx | 200 ++++++++++++++++++++++------- 1 file changed, 153 insertions(+), 47 deletions(-) diff --git a/components/trade/PerpPositions.tsx b/components/trade/PerpPositions.tsx index fe593087..8aa85514 100644 --- a/components/trade/PerpPositions.tsx +++ b/components/trade/PerpPositions.tsx @@ -46,16 +46,40 @@ const PerpPositions = () => { const { width } = useViewport() const showTableView = width ? width > breakpoints.md : false - const totalUnrealizedPnl = useMemo(() => { + const totalPnlStats = useMemo(() => { if (openPerpPositions.length && group !== undefined) { - return openPerpPositions - .map((position) => { - const market = group.getPerpMarketByMarketIndex(position.marketIndex) - return position.getUnRealizedPnlUi(market) - }) - .reduce((a, b) => a + b) + 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 0.0 + return { unrealized: 0, realized: 0, total: 0, unsettled: 0, roe: 0 } }, [openPerpPositions, group]) const handlePositionClick = (positionSize: number, market: PerpMarket) => { @@ -301,7 +325,10 @@ const PerpPositions = () => { ) })} {openPerpPositions.length > 0 ? ( - + <> @@ -315,25 +342,62 @@ const PerpPositions = () => { <> -
- - Total Unrealized PnL: - - - = 0 - ? 'text-th-up' - : 'text-th-down' - }`} - value={totalUnrealizedPnl} - isUsd - decimals={2} - /> +
+ + Total: + + } + delay={100} + > +
+ + = 0 + ? 'text-th-up' + : 'text-th-down' + }`} + value={totalPnlStats.unrealized} + isUsd + decimals={2} + /> + +
+
- + {!isUnownedAccount ? ( + + {/*
+ = 0 + ? 'text-th-up' + : 'text-th-down' + } + > + + %{' '} + + (ROE) + + +
*/} + + ) : null} + ) : null} @@ -600,28 +664,70 @@ const PerpPositions = () => { ) })} {openPerpPositions.length > 0 ? ( -
-
-
- - Total Unrealized PnL: - - 0 ? 'text-th-up' : 'text-th-down' - }`} - > - - -
-
-
+ <> + + {({ open }) => ( + <> + +
+
+ + Total Unrealized PnL: + + 0 + ? 'text-th-up' + : 'text-th-down' + }`} + > + + +
+ +
+ + + + Total ROE: + + = 0 + ? 'text-th-up' + : 'text-th-down' + }`} + > + + %{' '} + + + +
+
+ +
+ + )} +
+ ) : null}
) From ac9ff9f08f2a1492599b9ec7cfd8fb3448d2cc0e Mon Sep 17 00:00:00 2001 From: Lou-Kamades Date: Wed, 9 Aug 2023 22:29:17 -0500 Subject: [PATCH 3/3] move ROE into PnL tooltip --- components/shared/PnlTooltipContent.tsx | 10 +++++ components/stats/PerpPositionsStatsTable.tsx | 2 + components/trade/PerpPositions.tsx | 41 ++++---------------- public/locales/en/trade.json | 1 + public/locales/es/trade.json | 1 + public/locales/ru/trade.json | 1 + public/locales/zh/trade.json | 1 + public/locales/zh_tw/trade.json | 1 + 8 files changed, 24 insertions(+), 34 deletions(-) diff --git a/components/shared/PnlTooltipContent.tsx b/components/shared/PnlTooltipContent.tsx index 3eae7d0d..e8fcae90 100644 --- a/components/shared/PnlTooltipContent.tsx +++ b/components/shared/PnlTooltipContent.tsx @@ -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)}
+
+

{t('trade:return-on-equity')}

+ + + %{' '} + +
} delay={100} @@ -315,6 +316,7 @@ const PerpPositionsStatsTable = ({ realizedPnl={realizedPnl} totalPnl={totalPnl} unsettledPnl={unsettledPnl} + roe={roe} /> } delay={100} diff --git a/components/trade/PerpPositions.tsx b/components/trade/PerpPositions.tsx index 8aa85514..f87cbeb3 100644 --- a/components/trade/PerpPositions.tsx +++ b/components/trade/PerpPositions.tsx @@ -265,6 +265,7 @@ const PerpPositions = () => { realizedPnl={realizedPnl} totalPnl={totalPnl} unsettledPnl={unsettledPnl} + roe={roe} /> } delay={100} @@ -283,19 +284,6 @@ const PerpPositions = () => { /> - = 0 ? 'text-th-up' : 'text-th-down'} - > - - %{' '} - - (ROE) - - {!isUnownedAccount ? ( @@ -343,7 +331,7 @@ const PerpPositions = () => {
- + Total: { realizedPnl={totalPnlStats.realized} totalPnl={totalPnlStats.total} unsettledPnl={totalPnlStats.unsettled} + roe={totalPnlStats.roe} /> } delay={100} @@ -376,25 +365,8 @@ const PerpPositions = () => { {!isUnownedAccount ? ( - {/*
- = 0 - ? 'text-th-up' - : 'text-th-down' - } - > - - %{' '} - - (ROE) - - -
*/} + {' '} + <> ) : null} @@ -604,6 +576,7 @@ const PerpPositions = () => { realizedPnl={realizedPnl} totalPnl={totalPnl} unsettledPnl={unsettledPnl} + roe={roe} /> } delay={100} @@ -702,7 +675,7 @@ const PerpPositions = () => { Total ROE:
= 0 ? 'text-th-up' : 'text-th-down' diff --git a/public/locales/en/trade.json b/public/locales/en/trade.json index 29ffef05..3c3aead4 100644 --- a/public/locales/en/trade.json +++ b/public/locales/en/trade.json @@ -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", diff --git a/public/locales/es/trade.json b/public/locales/es/trade.json index 29ffef05..3c3aead4 100644 --- a/public/locales/es/trade.json +++ b/public/locales/es/trade.json @@ -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", diff --git a/public/locales/ru/trade.json b/public/locales/ru/trade.json index 29ffef05..3c3aead4 100644 --- a/public/locales/ru/trade.json +++ b/public/locales/ru/trade.json @@ -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", diff --git a/public/locales/zh/trade.json b/public/locales/zh/trade.json index 322ae6d4..af8dc387 100644 --- a/public/locales/zh/trade.json +++ b/public/locales/zh/trade.json @@ -71,6 +71,7 @@ "quote": "计价", "reduce": "Reduce", "reduce-only": "限减少", + "return-on-equity": "Return on Equity", "sells": "卖单", "settle-funds": "借清资金", "settle-funds-error": "借清出错", diff --git a/public/locales/zh_tw/trade.json b/public/locales/zh_tw/trade.json index 712236a3..266f82d6 100644 --- a/public/locales/zh_tw/trade.json +++ b/public/locales/zh_tw/trade.json @@ -72,6 +72,7 @@ "realized-pnl": "已實現的盈虧", "reduce": "Reduce", "reduce-only": "限減少", + "return-on-equity": "Return on Equity", "sells": "賣單", "settle-funds": "借清資金", "settle-funds-error": "借清出錯",