mango-v4-ui/components/trade/RecentTrades.tsx

221 lines
7.3 KiB
TypeScript
Raw Normal View History

2022-09-20 13:05:50 -07:00
import useInterval from '@components/shared/useInterval'
import mangoStore from '@store/mangoStore'
2023-01-02 19:14:23 -08:00
import { useEffect, useMemo } from 'react'
2022-09-20 13:05:50 -07:00
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
import Decimal from 'decimal.js'
import { ChartTradeType } from 'types'
import { useTranslation } from 'next-i18next'
2022-11-20 12:20:27 -08:00
import useSelectedMarket from 'hooks/useSelectedMarket'
2022-11-24 17:57:19 -08:00
import { Howl } from 'howler'
import { IconButton } from '@components/shared/Button'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { SOUND_SETTINGS_KEY } from 'utils/constants'
import { SpeakerWaveIcon, SpeakerXMarkIcon } from '@heroicons/react/20/solid'
import Tooltip from '@components/shared/Tooltip'
2022-11-27 15:07:45 -08:00
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
2023-01-02 19:14:23 -08:00
import usePrevious from '@components/shared/usePrevious'
2022-09-20 13:05:50 -07:00
2022-12-07 21:05:36 -08:00
const buySound = new Howl({
src: ['/sounds/trade-buy.mp3'],
volume: 0.5,
})
const sellSound = new Howl({
src: ['/sounds/trade-sell.mp3'],
volume: 0.5,
})
2022-09-20 13:05:50 -07:00
const RecentTrades = () => {
2022-11-20 20:52:03 -08:00
const { t } = useTranslation(['common', 'trade'])
const fills = mangoStore((s) => s.selectedMarket.fills)
2022-11-24 17:57:19 -08:00
const [soundSettings, setSoundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS
)
2023-01-02 19:14:23 -08:00
const previousFills = usePrevious(fills)
useEffect(() => {
if (!soundSettings['recent-trades']) return
if (fills.length && previousFills && previousFills.length) {
const latestFill: ChartTradeType = fills[0]
const previousFill: ChartTradeType = previousFills[0]
if (previousFill.orderId.toString() !== latestFill.orderId.toString()) {
const side =
latestFill.side || (latestFill.takerSide === 1 ? 'bid' : 'ask')
if (['buy', 'bid'].includes(side)) {
buySound.play()
} else {
sellSound.play()
}
}
}
}, [fills, previousFills, soundSettings])
2022-11-28 18:01:31 -08:00
2023-01-14 21:01:30 -08:00
const {
selectedMarket,
serumOrPerpMarket: market,
baseSymbol,
quoteSymbol,
} = useSelectedMarket()
2022-09-20 13:05:50 -07:00
// const fetchRecentTrades = useCallback(async () => {
// if (!market) return
2022-09-20 13:05:50 -07:00
// try {
// const response = await fetch(
// `https://event-history-api-candles.herokuapp.com/trades/address/${market.publicKey}`
// )
// const parsedResp = await response.json()
// const newTrades = parsedResp.data
// if (!newTrades) return null
2022-09-20 13:05:50 -07:00
// if (newTrades.length && trades.length === 0) {
// setTrades(newTrades)
// } else if (newTrades?.length && !isEqual(newTrades[0], trades[0])) {
// setTrades(newTrades)
// }
// } catch (e) {
// console.error('Unable to fetch recent trades', e)
// }
// }, [market, trades])
2022-09-20 13:05:50 -07:00
2022-11-20 20:52:03 -08:00
useEffect(() => {
// if (CLUSTER === 'mainnet-beta') {
// fetchRecentTrades()
// }
const actions = mangoStore.getState().actions
actions.loadMarketFills()
}, [selectedMarket])
2022-09-20 13:05:50 -07:00
useInterval(async () => {
// if (CLUSTER === 'mainnet-beta') {
// fetchRecentTrades()
// }
const actions = mangoStore.getState().actions
actions.loadMarketFills()
}, 5000)
2023-01-05 01:30:52 -08:00
const [buyRatio, sellRatio] = useMemo(() => {
if (!fills.length) return [0, 0]
2023-01-10 17:00:26 -08:00
const vol = fills.reduce(
(a: { buys: number; sells: number }, c: any) => {
if (c.side === 'buy' || c.takerSide === 1) {
a.buys = a.buys + c.size
} else {
a.sells = a.sells + c.size
}
return a
},
{ buys: 0, sells: 0 }
)
const totalVol = vol.buys + vol.sells
return [vol.buys / totalVol, vol.sells / totalVol]
2023-01-05 01:30:52 -08:00
}, [fills])
2022-09-20 13:05:50 -07:00
return (
2022-11-24 17:57:19 -08:00
<div className="thin-scroll h-full overflow-y-scroll">
2023-01-05 01:30:52 -08:00
<div className="flex items-center justify-between border-b border-th-bkg-3 py-1 px-2">
2022-11-24 17:57:19 -08:00
<Tooltip content={t('trade:trade-sounds-tooltip')} delay={250}>
<IconButton
onClick={() =>
setSoundSettings({
...soundSettings,
'recent-trades': !soundSettings['recent-trades'],
})
}
size="small"
hideBg
>
{soundSettings['recent-trades'] ? (
<SpeakerWaveIcon className="h-4 w-4 text-th-fgd-3" />
) : (
<SpeakerXMarkIcon className="h-4 w-4 text-th-fgd-3" />
)}
</IconButton>
</Tooltip>
2023-01-05 01:30:52 -08:00
<span className="text-xs text-th-fgd-4">
{t('trade:buys')}:{' '}
<span className="font-mono text-th-up">
{(buyRatio * 100).toFixed(1)}%
</span>
<span className="px-2">|</span>
{t('trade:sells')}:{' '}
<span className="font-mono text-th-down">
{(sellRatio * 100).toFixed(1)}%
</span>
</span>
2022-11-24 17:57:19 -08:00
</div>
<div className="px-2">
<table className="min-w-full">
<thead>
<tr className="text-right text-xxs text-th-fgd-4">
<th className="py-2 font-normal">{`${t(
'price'
)} (${quoteSymbol})`}</th>
<th className="py-2 font-normal">
{t('trade:size')} ({baseSymbol})
</th>
2022-12-18 13:04:50 -08:00
<th className="py-2 font-normal">{t('time')}</th>
2022-11-24 17:57:19 -08:00
</tr>
</thead>
<tbody>
{!!fills.length &&
fills.map((trade: ChartTradeType, i: number) => {
2023-01-02 19:14:23 -08:00
const side =
trade.side || (trade.takerSide === 1 ? 'bid' : 'ask')
2022-11-20 20:52:03 -08:00
2022-11-24 17:57:19 -08:00
// const price =
// typeof trade.price === 'number'
// ? trade.price
// : trade.price.toNumber()
const formattedPrice = market?.tickSize
2022-11-20 20:52:03 -08:00
? floorToDecimal(
2022-11-24 17:57:19 -08:00
trade.price,
getDecimalCount(market.tickSize)
2022-11-20 20:52:03 -08:00
)
2022-11-24 17:57:19 -08:00
: new Decimal(trade?.price || 0)
// const size = trade?.quantity?.toNumber() || trade?.size
const formattedSize =
market?.minOrderSize && trade.size
? floorToDecimal(
trade.size,
getDecimalCount(market.minOrderSize)
)
: new Decimal(trade.size || 0)
2022-11-20 20:52:03 -08:00
2022-11-24 17:57:19 -08:00
return (
<tr className="font-mono text-xs" key={i}>
<td
className={`pb-1.5 text-right ${
['buy', 'bid'].includes(side)
2023-01-02 19:14:23 -08:00
? 'text-th-up'
: 'text-th-down'
2022-11-24 17:57:19 -08:00
}`}
>
{formattedPrice.toFixed()}
</td>
<td className="pb-1.5 text-right">
{formattedSize.toFixed()}
</td>
2022-12-18 13:04:50 -08:00
<td className="pb-1.5 text-right text-th-fgd-4">
{trade.time
? new Date(trade.time).toLocaleTimeString()
: trade.timestamp
? new Date(
trade.timestamp.toNumber()
).toLocaleTimeString()
: '-'}
</td>
2022-11-24 17:57:19 -08:00
</tr>
)
})}
</tbody>
</table>
</div>
2022-09-22 14:45:25 -07:00
</div>
2022-09-20 13:05:50 -07:00
)
}
export default RecentTrades