Merge pull request #165 from blockworks-foundation/perp-volume-chart
add volume chart to perp stats
This commit is contained in:
commit
d8b66e390e
|
@ -3,15 +3,15 @@ import dayjs from 'dayjs'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { formatYAxis } from 'utils/formatting'
|
||||
// import { formatNumericValue } from 'utils/numbers'
|
||||
// import { usePerpFundingRate } from '@components/trade/PerpFundingRate'
|
||||
import { PerpStatsItem } from 'types'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
||||
import AverageFundingChart from './AverageFundingChart'
|
||||
|
||||
const CHART_WRAPPER_CLASSES = 'col-span-2 border-b border-th-bkg-3 py-4 px-6'
|
||||
const CHART_WRAPPER_CLASSES =
|
||||
'col-span-2 lg:col-span-1 border-b border-th-bkg-3 py-4 px-6'
|
||||
import PerpMarketParams from './PerpMarketParams'
|
||||
import PerpVolumeChart from './PerpVolumeChart'
|
||||
|
||||
const PerpMarketDetails = ({
|
||||
marketStats,
|
||||
|
@ -24,53 +24,22 @@ const PerpMarketDetails = ({
|
|||
const loadingPerpStats = mangoStore((s) => s.perpStats.loading)
|
||||
const [priceDaysToShow, setPriceDaysToShow] = useState('30')
|
||||
const [oiDaysToShow, setOiDaysToShow] = useState('30')
|
||||
// const [hourlyFundingeDaysToShow, setHourlyFundingDaysToShow] = useState('30')
|
||||
// const [instantFundingDaysToShow, setInstantFundingDaysToShow] = useState('30')
|
||||
// const rate = usePerpFundingRate()
|
||||
|
||||
const lastStat = useMemo(() => {
|
||||
if (!marketStats.length) return undefined
|
||||
return marketStats[marketStats.length - 1]
|
||||
}, [marketStats])
|
||||
|
||||
// const fundingRate = useMemo(() => {
|
||||
// if (!lastStat) return 0
|
||||
// if (rate?.isSuccess) {
|
||||
// const marketRate = rate?.data?.find(
|
||||
// (r) => r.market_index === perpMarket?.perpMarketIndex
|
||||
// )
|
||||
// return marketRate?.funding_rate_hourly
|
||||
// }
|
||||
// return lastStat.instantaneous_funding_rate
|
||||
// }, [rate, lastStat])
|
||||
|
||||
// const perpHourlyStats = useMemo(() => {
|
||||
// const latestStat = { ...lastStat } as PerpStatsItem
|
||||
// latestStat.instantaneous_funding_rate = fundingRate ? fundingRate : 0
|
||||
// latestStat.date_hour = dayjs().toISOString()
|
||||
// if (marketStats) {
|
||||
// const perpHourly = marketStats.concat([latestStat])
|
||||
// return perpHourly.map((stat) => ({
|
||||
// ...stat,
|
||||
// funding_rate_hourly: stat.funding_rate_hourly * 100,
|
||||
// }))
|
||||
// }
|
||||
// }, [marketStats, fundingRate])
|
||||
|
||||
// const instantFundingRateStats = useMemo(() => {
|
||||
// if (marketStats) {
|
||||
// return marketStats.map((stat) => ({
|
||||
// ...stat,
|
||||
// instantaneous_funding_rate: stat.instantaneous_funding_rate * 100,
|
||||
// }))
|
||||
// }
|
||||
// return []
|
||||
// }, [marketStats])
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2">
|
||||
{marketStats?.length && lastStat ? (
|
||||
<>
|
||||
<div className={`${CHART_WRAPPER_CLASSES} lg:border-r`}>
|
||||
<PerpVolumeChart
|
||||
loading={loadingPerpStats}
|
||||
marketStats={marketStats}
|
||||
/>
|
||||
</div>
|
||||
<div className={CHART_WRAPPER_CLASSES}>
|
||||
<DetailedAreaOrBarChart
|
||||
data={marketStats.concat([
|
||||
|
@ -93,42 +62,7 @@ const PerpMarketDetails = ({
|
|||
yKey={'open_interest'}
|
||||
/>
|
||||
</div>
|
||||
{/* old funding charts */}
|
||||
{/* <div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1">
|
||||
<DetailedAreaOrBarChart
|
||||
data={perpHourlyStats ? perpHourlyStats : []}
|
||||
daysToShow={hourlyFundingeDaysToShow}
|
||||
setDaysToShow={setHourlyFundingDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
suffix="%"
|
||||
tickFormat={(x) => formatNumericValue(x, 4)}
|
||||
title={t('trade:hourly-funding')}
|
||||
xKey="date_hour"
|
||||
yKey={'funding_rate_hourly'}
|
||||
yDecimals={5}
|
||||
showZeroLine
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:pl-6">
|
||||
<DetailedAreaOrBarChart
|
||||
data={instantFundingRateStats}
|
||||
daysToShow={instantFundingDaysToShow}
|
||||
setDaysToShow={setInstantFundingDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
suffix="%"
|
||||
tickFormat={(x) => formatNumericValue(x, 4)}
|
||||
title={t('trade:instantaneous-funding')}
|
||||
xKey="date_hour"
|
||||
yKey={'instantaneous_funding_rate'}
|
||||
yDecimals={5}
|
||||
showZeroLine
|
||||
/>
|
||||
</div> */}
|
||||
<div className={CHART_WRAPPER_CLASSES}>
|
||||
<div className={`${CHART_WRAPPER_CLASSES} lg:border-r`}>
|
||||
<AverageFundingChart
|
||||
loading={loadingPerpStats}
|
||||
marketStats={marketStats}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import { useMemo, useState } from 'react'
|
||||
import { GroupedDataItem, PerpStatsItem } from 'types'
|
||||
import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { formatYAxis } from 'utils/formatting'
|
||||
|
||||
const PerpVolumeChart = ({
|
||||
loading,
|
||||
marketStats,
|
||||
}: {
|
||||
loading: boolean
|
||||
marketStats: PerpStatsItem[]
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'stats', 'trade'])
|
||||
const [daysToShow, setDaysToShow] = useState('30')
|
||||
|
||||
const groupArrayByHours = (data: PerpStatsItem[], hours: number) => {
|
||||
const groupedData = []
|
||||
let currentGroup: GroupedDataItem[] = []
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const obj = data[i]
|
||||
const date = new Date(obj.date_hour)
|
||||
|
||||
if (hours === 24) {
|
||||
const day = date.getDate()
|
||||
const month = date.getMonth()
|
||||
if (
|
||||
currentGroup.length === 0 ||
|
||||
(currentGroup[0].day === day && currentGroup[0].month === month)
|
||||
) {
|
||||
currentGroup.push({ ...obj, day: day, month: month })
|
||||
} else {
|
||||
groupedData.push(currentGroup)
|
||||
currentGroup = [{ ...obj, day: day, month: month }]
|
||||
}
|
||||
} else {
|
||||
const intervalMillis = hours * 60 * 60 * 1000
|
||||
const timestamp = date.getTime()
|
||||
if (
|
||||
currentGroup.length === 0 ||
|
||||
timestamp - currentGroup[0].timestamp <= intervalMillis
|
||||
) {
|
||||
currentGroup.push({ ...obj, timestamp: timestamp })
|
||||
} else {
|
||||
groupedData.push(currentGroup)
|
||||
currentGroup = [{ ...obj, timestamp: timestamp }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentGroup.length > 0) {
|
||||
groupedData.push(currentGroup)
|
||||
}
|
||||
return groupedData
|
||||
}
|
||||
|
||||
const interval = useMemo(() => {
|
||||
if (daysToShow === '30') {
|
||||
return 24
|
||||
} else if (daysToShow === '7') {
|
||||
return 6
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}, [daysToShow])
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (!marketStats) return []
|
||||
const chartData = []
|
||||
if (interval !== 1) {
|
||||
const groupedData = groupArrayByHours(marketStats, interval)
|
||||
for (let i = 0; i < groupedData.length; i++) {
|
||||
const volume =
|
||||
groupedData[i][groupedData[i].length - 1].cumulative_quote_volume -
|
||||
groupedData[i][0].cumulative_quote_volume
|
||||
chartData.push({
|
||||
date_hour: groupedData[i][groupedData[i].length - 1].date_hour,
|
||||
volume: volume,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < marketStats.length; i++) {
|
||||
const volume =
|
||||
marketStats[i].cumulative_quote_volume -
|
||||
(marketStats[i - 1] ? marketStats[i - 1].cumulative_quote_volume : 0)
|
||||
chartData.push({
|
||||
date_hour: marketStats[i].date_hour,
|
||||
volume: volume,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return chartData
|
||||
}, [daysToShow, interval, marketStats])
|
||||
|
||||
return (
|
||||
<DetailedAreaOrBarChart
|
||||
data={chartData}
|
||||
daysToShow={daysToShow}
|
||||
setDaysToShow={setDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loading}
|
||||
loaderHeightClass="h-[350px]"
|
||||
prefix="$"
|
||||
tickFormat={(x) => formatYAxis(x)}
|
||||
title={t('stats:volume')}
|
||||
xKey="date_hour"
|
||||
yKey="volume"
|
||||
// yDecimals={5}
|
||||
chartType="bar"
|
||||
tooltipDateFormat={daysToShow === '30' ? 'DD MMM YY' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default PerpVolumeChart
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -302,6 +302,8 @@ export interface PerpStatsItem {
|
|||
total_fees: number
|
||||
}
|
||||
|
||||
export type GroupedDataItem = PerpStatsItem & Record<string, any>
|
||||
|
||||
export type ActivityFeed = {
|
||||
activity_type: string
|
||||
block_datetime: string
|
||||
|
|
Loading…
Reference in New Issue