2021-08-04 11:33:13 -07:00
|
|
|
import useMangoStore, { mangoClient } from '../stores/useMangoStore'
|
2021-06-27 21:44:48 -07:00
|
|
|
import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table'
|
2021-07-14 12:41:54 -07:00
|
|
|
import {
|
|
|
|
getMarketByPublicKey,
|
|
|
|
nativeI80F48ToUi,
|
2021-07-24 11:12:52 -07:00
|
|
|
PerpAccount,
|
2021-07-14 12:41:54 -07:00
|
|
|
PerpMarket,
|
2021-08-03 16:32:24 -07:00
|
|
|
QUOTE_INDEX,
|
2021-08-14 13:05:31 -07:00
|
|
|
ZERO_BN,
|
2021-07-14 12:41:54 -07:00
|
|
|
} from '@blockworks-foundation/mango-client'
|
2021-07-24 11:12:52 -07:00
|
|
|
import Button from './Button'
|
|
|
|
import { notify } from '../utils/notifications'
|
|
|
|
import SideBadge from './SideBadge'
|
|
|
|
import { useState } from 'react'
|
|
|
|
import Loading from './Loading'
|
2021-08-15 06:31:59 -07:00
|
|
|
import { formatUsdValue, usdFormatter } from '../utils'
|
2021-08-14 11:16:15 -07:00
|
|
|
import useTradeHistory from '../hooks/useTradeHistory'
|
2021-08-16 14:35:16 -07:00
|
|
|
import Tooltip from './Tooltip'
|
|
|
|
import { SettlePnlTooltip } from './MarketPosition'
|
2021-08-22 05:45:10 -07:00
|
|
|
import usePerpPositions from '../hooks/usePerpPositions'
|
2021-08-14 11:16:15 -07:00
|
|
|
|
2021-08-14 13:05:31 -07:00
|
|
|
export function getAvgEntryPrice(
|
2021-08-14 11:16:15 -07:00
|
|
|
mangoAccount,
|
|
|
|
perpAccount,
|
|
|
|
perpMarket,
|
|
|
|
perpTradeHistory
|
|
|
|
) {
|
|
|
|
let avgEntryPrice = '--'
|
|
|
|
if (perpTradeHistory.length) {
|
|
|
|
try {
|
2021-08-15 06:31:59 -07:00
|
|
|
avgEntryPrice = formatUsdValue(
|
|
|
|
perpAccount.getAverageOpenPrice(
|
|
|
|
mangoAccount,
|
|
|
|
perpMarket,
|
|
|
|
perpTradeHistory
|
|
|
|
)
|
|
|
|
)
|
2021-08-14 11:16:15 -07:00
|
|
|
} catch {
|
|
|
|
avgEntryPrice = '--'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return avgEntryPrice
|
|
|
|
}
|
|
|
|
|
2021-08-14 13:05:31 -07:00
|
|
|
export function getBreakEvenPrice(
|
2021-08-14 11:16:15 -07:00
|
|
|
mangoAccount,
|
|
|
|
perpAccount,
|
|
|
|
perpMarket,
|
|
|
|
perpTradeHistory
|
|
|
|
) {
|
|
|
|
let breakEvenPrice = '--'
|
|
|
|
if (perpTradeHistory.length) {
|
|
|
|
try {
|
2021-08-15 06:31:59 -07:00
|
|
|
breakEvenPrice = formatUsdValue(
|
|
|
|
perpAccount.getBreakEvenPrice(
|
|
|
|
mangoAccount,
|
|
|
|
perpMarket,
|
|
|
|
perpTradeHistory
|
|
|
|
)
|
|
|
|
)
|
2021-08-14 11:16:15 -07:00
|
|
|
} catch {
|
|
|
|
breakEvenPrice = '--'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return breakEvenPrice
|
|
|
|
}
|
2021-06-27 21:44:48 -07:00
|
|
|
|
|
|
|
const PositionsTable = () => {
|
2021-07-24 11:12:52 -07:00
|
|
|
const actions = useMangoStore((s) => s.actions)
|
2021-07-06 21:34:21 -07:00
|
|
|
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)
|
2021-07-14 12:41:54 -07:00
|
|
|
const allMarkets = useMangoStore((s) => s.selectedMangoGroup.markets)
|
2021-07-24 11:12:52 -07:00
|
|
|
const [settlingPerpAcc, setSettlingPerpAcc] = useState(null)
|
2021-08-14 11:16:15 -07:00
|
|
|
const tradeHistory = useTradeHistory()
|
2021-08-20 04:51:29 -07:00
|
|
|
const setMangoStore = useMangoStore((s) => s.set)
|
2021-08-22 05:45:10 -07:00
|
|
|
const perpPositions = usePerpPositions()
|
2021-06-27 21:44:48 -07:00
|
|
|
|
2021-07-24 11:12:52 -07:00
|
|
|
const handleSettlePnl = async (
|
|
|
|
perpMarket: PerpMarket,
|
|
|
|
perpAccount: PerpAccount
|
|
|
|
) => {
|
|
|
|
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
|
|
|
|
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
|
|
|
const wallet = useMangoStore.getState().wallet.current
|
|
|
|
const marketIndex = mangoGroup.getPerpMarketIndex(perpMarket.publicKey)
|
|
|
|
setSettlingPerpAcc(perpAccount)
|
|
|
|
try {
|
|
|
|
const txid = await mangoClient.settlePnl(
|
|
|
|
mangoGroup,
|
2021-08-20 13:39:44 -07:00
|
|
|
mangoCache,
|
2021-07-24 11:12:52 -07:00
|
|
|
mangoAccount,
|
|
|
|
perpMarket,
|
|
|
|
mangoGroup.rootBankAccounts[QUOTE_INDEX],
|
|
|
|
mangoCache.priceCache[marketIndex].price,
|
|
|
|
wallet
|
|
|
|
)
|
|
|
|
actions.fetchMangoAccounts()
|
|
|
|
notify({
|
|
|
|
title: 'Successfully settled PNL',
|
|
|
|
description: '',
|
|
|
|
txid,
|
|
|
|
})
|
|
|
|
} catch (e) {
|
|
|
|
console.log('Error settling PNL: ', `${e}`, `${perpAccount}`)
|
|
|
|
notify({
|
|
|
|
title: 'Error settling PNL',
|
|
|
|
description: e.message,
|
|
|
|
txid: e.txid,
|
|
|
|
type: 'error',
|
|
|
|
})
|
|
|
|
} finally {
|
|
|
|
setSettlingPerpAcc(null)
|
|
|
|
}
|
|
|
|
}
|
2021-06-27 21:44:48 -07:00
|
|
|
|
2021-08-20 04:51:29 -07:00
|
|
|
const handleSizeClick = (size) => {
|
|
|
|
setMangoStore((state) => {
|
|
|
|
state.tradeForm.baseSize = size
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-06-27 21:44:48 -07:00
|
|
|
return (
|
2021-08-23 07:14:03 -07:00
|
|
|
<div className="flex flex-col pb-2 pt-4">
|
2021-07-24 11:12:52 -07:00
|
|
|
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
|
|
|
<div className="align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
2021-08-22 05:45:10 -07:00
|
|
|
{perpPositions.length ? (
|
2021-07-24 11:12:52 -07:00
|
|
|
<div className="overflow-hidden border-b border-th-bkg-2 sm:rounded-m">
|
|
|
|
<Table className="min-w-full divide-y divide-th-bkg-2">
|
2021-06-27 21:44:48 -07:00
|
|
|
<Thead>
|
2021-07-29 06:19:32 -07:00
|
|
|
<Tr className="text-th-fgd-3 text-xs">
|
2021-07-24 11:12:52 -07:00
|
|
|
<Th scope="col" className="px-6 py-2 text-left font-normal">
|
2021-07-29 06:19:32 -07:00
|
|
|
Market
|
2021-06-27 21:44:48 -07:00
|
|
|
</Th>
|
2021-07-24 11:12:52 -07:00
|
|
|
<Th scope="col" className="px-2 py-2 text-left font-normal">
|
|
|
|
Side
|
|
|
|
</Th>
|
|
|
|
<Th scope="col" className="px-2 py-2 text-left font-normal">
|
2021-08-14 13:05:31 -07:00
|
|
|
Position size
|
2021-08-14 11:16:15 -07:00
|
|
|
</Th>
|
|
|
|
<Th scope="col" className="px-2 py-2 text-left font-normal">
|
2021-08-14 13:05:31 -07:00
|
|
|
Notional size
|
2021-06-27 21:44:48 -07:00
|
|
|
</Th>
|
2021-07-24 11:12:52 -07:00
|
|
|
<Th scope="col" className="px-2 py-2 text-left font-normal">
|
2021-08-14 11:16:15 -07:00
|
|
|
Avg entry price
|
2021-06-27 21:44:48 -07:00
|
|
|
</Th>
|
2021-07-24 11:12:52 -07:00
|
|
|
<Th scope="col" className="px-2 py-2 text-left font-normal">
|
2021-08-14 11:16:15 -07:00
|
|
|
Break-even price
|
|
|
|
</Th>
|
|
|
|
<Th scope="col" className="px-2 py-2 text-left font-normal">
|
2021-08-16 14:35:16 -07:00
|
|
|
<Tooltip content={<SettlePnlTooltip />}>
|
|
|
|
<Tooltip.Content>Unsettled PnL</Tooltip.Content>
|
|
|
|
</Tooltip>
|
2021-06-27 21:44:48 -07:00
|
|
|
</Th>
|
2021-07-24 11:12:52 -07:00
|
|
|
<Th scope="col" className={`relative px-6 py-2.5`}>
|
|
|
|
<span className={`sr-only`}>Edit</span>
|
|
|
|
</Th>
|
2021-06-27 21:44:48 -07:00
|
|
|
</Tr>
|
|
|
|
</Thead>
|
|
|
|
<Tbody>
|
2021-08-22 05:45:10 -07:00
|
|
|
{perpPositions.map(({ perpAccount, marketIndex }, index) => {
|
|
|
|
const perpMarketInfo = mangoGroup.perpMarkets[marketIndex]
|
|
|
|
const marketConfig = getMarketByPublicKey(
|
|
|
|
groupConfig,
|
|
|
|
perpMarketInfo.perpMarket
|
|
|
|
)
|
|
|
|
const perpMarketCache =
|
|
|
|
mangoCache.perpMarketCache[marketIndex]
|
|
|
|
const price = mangoCache.priceCache[marketIndex].price
|
|
|
|
const perpMarket = allMarkets[
|
|
|
|
marketConfig.publicKey.toString()
|
|
|
|
] as PerpMarket
|
|
|
|
const perpTradeHistory = tradeHistory.filter(
|
|
|
|
(t) => t.marketName === marketConfig.name
|
|
|
|
)
|
2021-07-06 21:34:21 -07:00
|
|
|
|
2021-08-22 05:45:10 -07:00
|
|
|
return (
|
|
|
|
<Tr
|
|
|
|
key={`${marketIndex}`}
|
|
|
|
className={`border-b border-th-bkg-3
|
2021-07-24 13:15:27 -07:00
|
|
|
${index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
|
2021-06-27 21:44:48 -07:00
|
|
|
`}
|
2021-08-22 05:45:10 -07:00
|
|
|
>
|
|
|
|
<Td className="px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1">
|
|
|
|
<div className="flex items-center">
|
|
|
|
<img
|
|
|
|
alt=""
|
|
|
|
width="20"
|
|
|
|
height="20"
|
|
|
|
src={`/assets/icons/${marketConfig.baseSymbol.toLowerCase()}.svg`}
|
|
|
|
className={`mr-2.5`}
|
|
|
|
/>
|
|
|
|
<div>{marketConfig.name}</div>
|
|
|
|
</div>
|
|
|
|
</Td>
|
|
|
|
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
|
|
|
|
{!perpAccount.basePosition.eq(ZERO_BN) ? (
|
|
|
|
<SideBadge
|
|
|
|
side={
|
|
|
|
perpAccount.basePosition.gt(ZERO_BN)
|
|
|
|
? 'long'
|
|
|
|
: 'short'
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
'-'
|
|
|
|
)}
|
|
|
|
</Td>
|
|
|
|
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
|
|
|
|
{perpAccount &&
|
|
|
|
Math.abs(
|
|
|
|
perpMarket.baseLotsToNumber(
|
|
|
|
perpAccount.basePosition
|
|
|
|
)
|
|
|
|
) > 0 ? (
|
|
|
|
<span
|
|
|
|
className="cursor-pointer underline hover:no-underline"
|
|
|
|
onClick={() =>
|
|
|
|
handleSizeClick(
|
|
|
|
Math.abs(
|
|
|
|
perpMarket.baseLotsToNumber(
|
|
|
|
perpAccount.basePosition
|
2021-08-20 04:51:29 -07:00
|
|
|
)
|
|
|
|
)
|
2021-08-22 05:45:10 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{`${Math.abs(
|
2021-08-19 17:14:04 -07:00
|
|
|
perpMarket.baseLotsToNumber(
|
|
|
|
perpAccount.basePosition
|
2021-08-22 05:45:10 -07:00
|
|
|
)
|
|
|
|
)} ${marketConfig.baseSymbol}`}
|
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
`0 ${marketConfig.baseSymbol}`
|
|
|
|
)}
|
|
|
|
</Td>
|
|
|
|
<Th
|
|
|
|
scope="col"
|
|
|
|
className="px-2 py-2 text-left font-normal"
|
|
|
|
>
|
|
|
|
{usdFormatter(
|
|
|
|
Math.abs(
|
|
|
|
perpMarket.baseLotsToNumber(
|
|
|
|
perpAccount.basePosition
|
|
|
|
) *
|
|
|
|
mangoGroup
|
|
|
|
.getPrice(marketIndex, mangoCache)
|
|
|
|
.toNumber()
|
|
|
|
)
|
|
|
|
)}
|
|
|
|
</Th>
|
|
|
|
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
|
|
|
|
{getAvgEntryPrice(
|
|
|
|
mangoAccount,
|
|
|
|
perpAccount,
|
|
|
|
perpMarket,
|
|
|
|
perpTradeHistory
|
|
|
|
)}
|
|
|
|
</Td>
|
|
|
|
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
|
|
|
|
{getBreakEvenPrice(
|
|
|
|
mangoAccount,
|
|
|
|
perpAccount,
|
|
|
|
perpMarket,
|
|
|
|
perpTradeHistory
|
|
|
|
)}
|
|
|
|
</Td>
|
|
|
|
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
|
|
|
|
{usdFormatter(
|
|
|
|
+nativeI80F48ToUi(
|
|
|
|
perpAccount.getPnl(
|
|
|
|
perpMarketInfo,
|
|
|
|
perpMarketCache,
|
|
|
|
price
|
|
|
|
),
|
|
|
|
marketConfig.quoteDecimals
|
|
|
|
)
|
|
|
|
)}
|
|
|
|
</Td>
|
|
|
|
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
|
|
|
|
<div className="flex justify-end">
|
|
|
|
<Button
|
|
|
|
onClick={() =>
|
|
|
|
handleSettlePnl(perpMarket, perpAccount)
|
|
|
|
}
|
|
|
|
className="ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3"
|
|
|
|
>
|
|
|
|
{settlingPerpAcc == perpAccount ? (
|
|
|
|
<Loading />
|
|
|
|
) : (
|
|
|
|
<span>Settle PNL</span>
|
|
|
|
)}
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</Td>
|
|
|
|
</Tr>
|
|
|
|
)
|
|
|
|
})}
|
2021-06-27 21:44:48 -07:00
|
|
|
</Tbody>
|
|
|
|
</Table>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<div
|
|
|
|
className={`w-full text-center py-6 bg-th-bkg-1 text-th-fgd-3 rounded-md`}
|
|
|
|
>
|
2021-08-02 05:44:58 -07:00
|
|
|
No perp positions
|
2021-06-27 21:44:48 -07:00
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default PositionsTable
|