2022-12-21 20:19:00 -08:00
import {
2023-08-28 04:59:36 -07:00
Bank ,
2022-12-21 20:19:00 -08:00
HealthType ,
MangoAccount ,
PerpMarket ,
Serum3Market ,
} from '@blockworks-foundation/mango-v4'
2023-01-24 16:54:24 -08:00
import FormatNumericValue from '@components/shared/FormatNumericValue'
2022-12-21 20:19:00 -08:00
import HealthImpact from '@components/shared/HealthImpact'
import Tooltip from '@components/shared/Tooltip'
import mangoStore from '@store/mangoStore'
2023-04-25 05:41:23 -07:00
import Decimal from 'decimal.js'
2022-12-21 20:19:00 -08:00
import useMangoGroup from 'hooks/useMangoGroup'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useTranslation } from 'next-i18next'
import { useMemo } from 'react'
import Slippage from './Slippage'
2023-06-29 04:38:32 -07:00
import {
floorToDecimal ,
formatNumericValue ,
2023-06-30 04:14:35 -07:00
getDecimalCount ,
2023-06-29 04:38:32 -07:00
} from 'utils/numbers'
2023-05-14 06:07:06 -07:00
import { formatTokenSymbol } from 'utils/tokens'
2023-06-30 04:14:35 -07:00
import useOpenPerpPositions from 'hooks/useOpenPerpPositions'
2023-11-05 03:22:05 -08:00
import {
TriggerOrderTypes ,
calculateEstPriceForBaseSize ,
} from 'utils/tradeForm'
2022-12-21 20:19:00 -08:00
const TradeSummary = ( {
2023-08-28 04:59:36 -07:00
balanceBank ,
2022-12-21 20:19:00 -08:00
mangoAccount ,
} : {
2023-08-28 04:59:36 -07:00
balanceBank : Bank | undefined
2022-12-21 20:19:00 -08:00
mangoAccount : MangoAccount | undefined
} ) = > {
const { t } = useTranslation ( [ 'common' , 'trade' ] )
const { group } = useMangoGroup ( )
const tradeForm = mangoStore ( ( s ) = > s . tradeForm )
2023-07-06 07:24:07 -07:00
const orderbook = mangoStore ( ( s ) = > s . selectedMarket . orderbook )
2023-04-25 05:41:23 -07:00
const { selectedMarket , quoteBank } = useSelectedMarket ( )
2023-06-30 04:14:35 -07:00
const openPerpPositions = useOpenPerpPositions ( )
2023-06-29 04:38:32 -07:00
2023-06-30 04:14:35 -07:00
// calc new avg price if an open position exists
const avgEntryPrice = useMemo ( ( ) = > {
if (
2023-07-06 07:24:07 -07:00
! openPerpPositions ? . length ||
2023-06-30 04:14:35 -07:00
! selectedMarket ||
2023-07-06 07:24:07 -07:00
! orderbook ||
2023-06-30 04:14:35 -07:00
selectedMarket instanceof Serum3Market
)
return
const openPosition = openPerpPositions . find (
2023-07-21 11:47:53 -07:00
( pos ) = > pos . marketIndex === selectedMarket . perpMarketIndex ,
2023-06-30 04:14:35 -07:00
)
const { baseSize , price , reduceOnly , side , tradeType } = tradeForm
2023-07-06 07:24:07 -07:00
if ( ! openPosition || ! price || ! tradeForm . baseSize ) return
2023-06-30 04:14:35 -07:00
let orderPrice = parseFloat ( price )
if ( tradeType === 'Market' ) {
orderPrice = calculateEstPriceForBaseSize (
orderbook ,
parseFloat ( tradeForm . baseSize ) ,
2023-07-21 11:47:53 -07:00
tradeForm . side ,
2023-06-30 04:14:35 -07:00
)
}
const currentSize = openPosition . getBasePositionUi ( selectedMarket )
const tradeSize =
side === 'buy' ? parseFloat ( baseSize ) : parseFloat ( baseSize ) * - 1
const newTotalSize = currentSize + tradeSize
const currentAvgPrice = openPosition . getAverageEntryPriceUi ( selectedMarket )
2023-06-29 21:37:01 -07:00
2023-06-30 04:14:35 -07:00
// don't calc when closing position
if ( newTotalSize === 0 ) {
return
}
// don't calc when reducing position
if (
( currentSize < 0 !== tradeSize < 0 &&
newTotalSize < 0 === currentSize < 0 ) ||
reduceOnly
) {
return
}
// if trade side changes with new trade return new trade price
if ( currentSize < 0 !== newTotalSize < 0 ) {
return price
}
const newTotalCost = currentAvgPrice * currentSize + orderPrice * tradeSize
const newAvgEntryPrice = newTotalCost / newTotalSize
return newAvgEntryPrice
2023-07-06 07:24:07 -07:00
} , [ openPerpPositions , selectedMarket , tradeForm , orderbook ] )
2022-12-21 20:19:00 -08:00
const maintProjectedHealth = useMemo ( ( ) = > {
2023-03-04 10:53:49 -08:00
if ( ! mangoAccount || ! group ) return 100
2022-12-21 20:19:00 -08:00
let simulatedHealthRatio = 0
try {
if ( selectedMarket instanceof Serum3Market ) {
simulatedHealthRatio =
tradeForm . side === 'sell'
? mangoAccount . simHealthRatioWithSerum3AskUiChanges (
group ,
Number ( tradeForm . baseSize ) ,
selectedMarket . serumMarketExternal ,
2023-07-21 11:47:53 -07:00
HealthType . maint ,
2022-12-21 20:19:00 -08:00
)
: mangoAccount . simHealthRatioWithSerum3BidUiChanges (
group ,
Number ( tradeForm . quoteSize ) ,
selectedMarket . serumMarketExternal ,
2023-07-21 11:47:53 -07:00
HealthType . maint ,
2022-12-21 20:19:00 -08:00
)
} else if ( selectedMarket instanceof PerpMarket ) {
simulatedHealthRatio =
tradeForm . side === 'sell'
? mangoAccount . simHealthRatioWithPerpAskUiChanges (
group ,
selectedMarket . perpMarketIndex ,
2023-07-17 06:25:46 -07:00
parseFloat ( tradeForm . baseSize ) || 0 ,
2023-07-21 11:47:53 -07:00
HealthType . maint ,
2022-12-21 20:19:00 -08:00
)
: mangoAccount . simHealthRatioWithPerpBidUiChanges (
group ,
selectedMarket . perpMarketIndex ,
2023-07-17 06:25:46 -07:00
parseFloat ( tradeForm . baseSize ) || 0 ,
2023-07-21 11:47:53 -07:00
HealthType . maint ,
2022-12-21 20:19:00 -08:00
)
}
} catch ( e ) {
console . warn ( 'Error calculating projected health: ' , e )
}
return simulatedHealthRatio > 100
? 100
: simulatedHealthRatio < 0
? 0
: Math . trunc ( simulatedHealthRatio )
2023-01-02 11:50:09 -08:00
} , [ group , mangoAccount , selectedMarket , tradeForm ] )
2022-12-21 20:19:00 -08:00
2023-05-14 06:07:06 -07:00
const [ balance , borrowAmount ] = useMemo ( ( ) = > {
if ( ! balanceBank || ! mangoAccount ) return [ 0 , 0 ]
2023-03-17 05:48:38 -07:00
let borrowAmount
const balance = mangoAccount . getTokenDepositsUi ( balanceBank )
if ( tradeForm . side === 'buy' ) {
const remainingBalance = balance - parseFloat ( tradeForm . quoteSize )
borrowAmount = remainingBalance < 0 ? Math . abs ( remainingBalance ) : 0
} else {
const remainingBalance = balance - parseFloat ( tradeForm . baseSize )
borrowAmount = remainingBalance < 0 ? Math . abs ( remainingBalance ) : 0
}
2023-05-14 06:07:06 -07:00
return [ balance , borrowAmount ]
2023-03-17 05:48:38 -07:00
} , [ balanceBank , mangoAccount , tradeForm ] )
2023-04-25 05:41:23 -07:00
const orderValue = useMemo ( ( ) = > {
2023-04-28 11:39:22 -07:00
if (
! quoteBank ||
! tradeForm . price ||
2023-05-13 04:41:42 -07:00
! tradeForm . baseSize ||
2023-06-15 05:32:40 -07:00
isNaN ( parseFloat ( tradeForm . price ) ) ||
isNaN ( parseFloat ( tradeForm . baseSize ) )
2023-04-28 11:39:22 -07:00
)
return 0
2023-04-25 05:41:23 -07:00
const basePriceDecimal = new Decimal ( tradeForm . price )
const quotePriceDecimal = new Decimal ( quoteBank . uiPrice )
const sizeDecimal = new Decimal ( tradeForm . baseSize )
2023-05-13 04:41:42 -07:00
return floorToDecimal (
basePriceDecimal . mul ( quotePriceDecimal ) . mul ( sizeDecimal ) ,
2023-07-21 11:47:53 -07:00
2 ,
2023-05-13 04:41:42 -07:00
)
2023-04-25 05:41:23 -07:00
} , [ quoteBank , tradeForm ] )
2023-11-05 03:22:05 -08:00
const isTriggerOrder =
tradeForm . tradeType === TriggerOrderTypes . STOP_LOSS ||
tradeForm . tradeType === TriggerOrderTypes . TAKE_PROFIT
2022-12-21 20:19:00 -08:00
return (
< div className = "space-y-2 px-3 md:px-4" >
2022-12-30 10:26:45 -08:00
< div className = "flex justify-between text-xs" >
< p > { t ( 'trade:order-value' ) } < / p >
2023-07-03 06:17:17 -07:00
< p className = "font-mono text-th-fgd-2" >
2023-05-13 04:41:42 -07:00
{ orderValue ? < FormatNumericValue value = { orderValue } isUsd / > : '– ' }
2022-12-30 10:26:45 -08:00
< / p >
< / div >
2022-12-21 20:19:00 -08:00
< HealthImpact maintProjectedHealth = { maintProjectedHealth } small / >
2023-05-14 06:07:06 -07:00
{ borrowAmount && balanceBank ? (
< >
< div className = "flex justify-between text-xs" >
< Tooltip
content = {
balance
? t ( 'trade:tooltip-borrow-balance' , {
balance : formatNumericValue ( balance ) ,
borrowAmount : formatNumericValue ( borrowAmount ) ,
token : formatTokenSymbol ( balanceBank . name ) ,
rate : formatNumericValue (
balanceBank . getBorrowRateUi ( ) ,
2023-07-21 11:47:53 -07:00
2 ,
2023-05-14 06:07:06 -07:00
) ,
} )
: t ( 'trade:tooltip-borrow-no-balance' , {
borrowAmount : formatNumericValue ( borrowAmount ) ,
token : formatTokenSymbol ( balanceBank . name ) ,
rate : formatNumericValue (
balanceBank . getBorrowRateUi ( ) ,
2023-07-21 11:47:53 -07:00
2 ,
2023-05-14 06:07:06 -07:00
) ,
} )
2023-03-17 05:48:38 -07:00
}
2023-05-14 06:07:06 -07:00
delay = { 100 }
>
< p className = "tooltip-underline" > { t ( 'borrow-amount' ) } < / p >
< / Tooltip >
< p className = "text-right font-mono text-th-fgd-2" >
< FormatNumericValue
value = { borrowAmount }
decimals = { balanceBank . mintDecimals }
/ > { ' ' }
< span className = "font-body text-th-fgd-4" >
{ formatTokenSymbol ( balanceBank . name ) }
< / span >
< / p >
< / div >
< div className = "flex justify-between text-xs" >
< Tooltip
content = { t ( 'loan-origination-fee-tooltip' , {
fee : ` ${ (
balanceBank . loanOriginationFeeRate . toNumber ( ) * 100
) . toFixed ( 3 ) } % ` ,
} ) }
delay = { 100 }
>
< p className = "tooltip-underline" > { t ( 'loan-origination-fee' ) } < / p >
< / Tooltip >
< p className = "text-right font-mono text-th-fgd-2" >
< FormatNumericValue
value = {
borrowAmount * balanceBank . loanOriginationFeeRate . toNumber ( )
}
decimals = { balanceBank . mintDecimals }
/ > { ' ' }
< span className = "font-body text-th-fgd-4" >
{ formatTokenSymbol ( balanceBank . name ) }
< / span >
< / p >
< / div >
< / >
2023-03-17 05:48:38 -07:00
) : null }
2023-05-14 06:07:06 -07:00
{ / * < d i v c l a s s N a m e = " f l e x j u s t i f y - b e t w e e n t e x t - x s " >
2022-12-21 20:19:00 -08:00
< Tooltip content = "The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw." >
< p className = "tooltip-underline" > { t ( 'free-collateral' ) } < / p >
< / Tooltip >
< p className = "text-th-fgd-2" >
2023-01-24 16:54:24 -08:00
{ group && mangoAccount ? (
< FormatNumericValue
value = { toUiDecimalsForQuote (
mangoAccount . getCollateralValue ( group )
) }
decimals = { 2 }
isUsd
/ >
) : (
'– '
) }
2022-12-21 20:19:00 -08:00
< / p >
2023-05-14 06:07:06 -07:00
< / div > * / }
2022-12-21 20:19:00 -08:00
< Slippage / >
2023-06-30 04:14:35 -07:00
{ avgEntryPrice && selectedMarket instanceof PerpMarket ? (
2023-06-29 04:38:32 -07:00
< div className = "flex justify-between text-xs" >
< p > { t ( 'trade:avg-entry-price' ) } < / p >
< p className = "text-th-fgd-2" >
< FormatNumericValue
value = { avgEntryPrice }
decimals = { getDecimalCount ( selectedMarket . tickSize ) }
isUsd
/ >
< / p >
< / div >
2023-06-30 04:14:35 -07:00
) : null }
2023-11-05 03:22:05 -08:00
{ selectedMarket instanceof Serum3Market && ! isTriggerOrder ? (
2023-08-19 01:50:59 -07:00
< div className = "flex justify-between text-xs" >
< p > { t ( 'common:route' ) } < / p >
< p className = "text-th-fgd-2" > Openbook < / p >
< / div >
) : null }
2022-12-21 20:19:00 -08:00
< / div >
)
}
export default TradeSummary