From 1e46d261413bb17ff9ea7105a2cb98c8f07efb85 Mon Sep 17 00:00:00 2001 From: saml33 Date: Thu, 23 Nov 2023 22:26:46 +1100 Subject: [PATCH] re-arrange layout --- components/stats/mango/DepositsAndBorrows.tsx | 67 +++++ components/stats/mango/Fees.tsx | 175 ++++++++++++ components/stats/mango/Liquidations.tsx | 24 ++ components/stats/mango/LiquidationsCharts.tsx | 21 -- .../stats/mango/MangoPerpStatsCharts.tsx | 264 ------------------ components/stats/mango/MangoStats.tsx | 18 +- components/stats/mango/OpenInterest.tsx | 39 +++ components/stats/mango/SwapVolumeChart.tsx | 30 ++ components/stats/mango/TokenStatsCharts.tsx | 155 ---------- components/stats/mango/Volume.tsx | 121 ++++++++ hooks/usePerpStatsChartData.ts | 90 ++++++ 11 files changed, 556 insertions(+), 448 deletions(-) create mode 100644 components/stats/mango/DepositsAndBorrows.tsx create mode 100644 components/stats/mango/Fees.tsx create mode 100644 components/stats/mango/Liquidations.tsx delete mode 100644 components/stats/mango/LiquidationsCharts.tsx delete mode 100644 components/stats/mango/MangoPerpStatsCharts.tsx create mode 100644 components/stats/mango/OpenInterest.tsx create mode 100644 components/stats/mango/SwapVolumeChart.tsx delete mode 100644 components/stats/mango/TokenStatsCharts.tsx create mode 100644 components/stats/mango/Volume.tsx create mode 100644 hooks/usePerpStatsChartData.ts diff --git a/components/stats/mango/DepositsAndBorrows.tsx b/components/stats/mango/DepositsAndBorrows.tsx new file mode 100644 index 00000000..61c918a7 --- /dev/null +++ b/components/stats/mango/DepositsAndBorrows.tsx @@ -0,0 +1,67 @@ +import mangoStore from '@store/mangoStore' +import { useTranslation } from 'next-i18next' +import { useEffect, useState } from 'react' +import { formatYAxis } from 'utils/formatting' +import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart' +import NetDepositsChart from './NetDepositsChart' + +const DepositsAndBorrows = () => { + const { t } = useTranslation(['common', 'token', 'trade']) + const mangoStats = mangoStore((s) => s.tokenStats.mangoStats) + const loadingStats = mangoStore((s) => s.tokenStats.loading) + const [borrowDaysToShow, setBorrowDaysToShow] = useState('30') + const [depositDaysToShow, setDepositDaysToShow] = useState('30') + const tokenStatsInitialLoad = mangoStore((s) => s.tokenStats.initialLoad) + + useEffect(() => { + if (!tokenStatsInitialLoad) { + const actions = mangoStore.getState().actions + actions.fetchTokenStats() + } + }, [tokenStatsInitialLoad]) + + return ( + <> +

Deposits and Borrows

+
+
+ `$${formatYAxis(x)}`} + title={t('total-deposit-value')} + xKey="date" + yKey={'depositValue'} + /> +
+
+ `$${formatYAxis(x)}`} + title={t('total-borrow-value')} + xKey="date" + yKey={'borrowValue'} + /> +
+
+
+ +
+ + ) +} + +export default DepositsAndBorrows diff --git a/components/stats/mango/Fees.tsx b/components/stats/mango/Fees.tsx new file mode 100644 index 00000000..8f1ac764 --- /dev/null +++ b/components/stats/mango/Fees.tsx @@ -0,0 +1,175 @@ +import Switch from '@components/forms/Switch' +import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart' +import mangoStore from '@store/mangoStore' +import usePerpStatsChartData from 'hooks/usePerpStatsChartData' +import { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { MangoTokenStatsItem } from 'types' +import { formatYAxis } from 'utils/formatting' +import { groupPerpByHourlyInterval } from './Volume' + +interface GroupedTokenDataItem extends MangoTokenStatsItem { + intervalStartMillis: number +} + +const groupTokenByHourlyInterval = ( + data: MangoTokenStatsItem[], + intervalDurationHours: number, +) => { + const intervalMillis = intervalDurationHours * 60 * 60 * 1000 + const groupedData = [] + let currentGroup: GroupedTokenDataItem | null = null + for (let i = 0; i < data.length; i++) { + const obj = data[i] + const date = new Date(obj.date) + const intervalStartMillis = + Math.floor(date.getTime() / intervalMillis) * intervalMillis + if ( + !currentGroup || + currentGroup.intervalStartMillis !== intervalStartMillis + ) { + currentGroup = { + ...obj, + intervalStartMillis: intervalStartMillis, + } + groupedData.push(currentGroup) + } else { + currentGroup.feesCollected += obj.feesCollected + } + } + return groupedData +} + +const Fees = () => { + const { t } = useTranslation(['common', 'token', 'trade']) + const mangoStats = mangoStore((s) => s.tokenStats.mangoStats) + const loadingStats = mangoStore((s) => s.tokenStats.loading) + const loadingPerpStats = mangoStore((s) => s.perpStats.loading) + const [feesDaysToShow, setFeesDaysToShow] = useState('30') + const [showCumulativeFees, setShowCumulativeFees] = useState(true) + const [feesPerpDaysToShow, setFeesPerpDaysToShow] = useState('30') + const [showCumulativePerpFees, setShowCumulativePerpFees] = useState(true) + const { feeValues: perpFeeChartData } = usePerpStatsChartData() + + const tokenFeesChartData = useMemo(() => { + if (!mangoStats.length) return [] + if (showCumulativeFees) { + return mangoStats + } else { + const transformedData = [] + for (let i = 1; i < mangoStats.length; i++) { + const currentInterval = { ...mangoStats[i] } + const previousInterval = mangoStats[i - 1] + + // Calculate the absolute fees for the current interval + currentInterval.feesCollected = + currentInterval.feesCollected - previousInterval.feesCollected + + transformedData.push(currentInterval) + } + transformedData.unshift(mangoStats[0]) + + if (feesDaysToShow === '30') { + return groupTokenByHourlyInterval(transformedData, 24) + } else if (feesDaysToShow === '7') { + return groupTokenByHourlyInterval(transformedData, 4) + } else return transformedData + } + }, [mangoStats, feesDaysToShow, showCumulativeFees]) + + const perpFeeValues = useMemo(() => { + if (!perpFeeChartData || !perpFeeChartData.length) return [] + + let feeChartData = perpFeeChartData + if (!showCumulativePerpFees) { + const transformedData = [] + for (let i = 1; i < perpFeeChartData.length; i++) { + const currentInterval = { ...perpFeeChartData[i] } + const previousInterval = perpFeeChartData[i - 1] + + // Calculate the absolute fees for the current interval + currentInterval.value = currentInterval.value - previousInterval.value + + transformedData.push(currentInterval) + } + transformedData.unshift(perpFeeChartData[0]) + + if (feesDaysToShow === '30') { + feeChartData = groupPerpByHourlyInterval(transformedData, 24) + } else if (feesDaysToShow === '7') { + feeChartData = groupPerpByHourlyInterval(transformedData, 4) + } else feeChartData = transformedData + } + + return feeChartData + }, [feesDaysToShow, perpFeeChartData, showCumulativePerpFees]) + + return ( + <> +

Fees

+
+
+
+ `$${formatYAxis(x)}`} + title={t('token:token-fees-collected')} + tooltipContent={t('token:tooltip-token-fees-collected')} + xKey="date" + yKey={'feesCollected'} + chartType={showCumulativeFees ? 'area' : 'bar'} + /> +
+
+ setShowCumulativeFees(!showCumulativeFees)} + small + > + {t('stats:show-cumulative')} + +
+
+
+
+ `$${formatYAxis(x)}`} + title="Perp Fees" + xKey="date" + yKey="value" + chartType={showCumulativePerpFees ? 'area' : 'bar'} + /> +
+
+ + setShowCumulativePerpFees(!showCumulativePerpFees) + } + small + > + {t('stats:show-cumulative')} + +
+
+
+ + ) +} + +export default Fees diff --git a/components/stats/mango/Liquidations.tsx b/components/stats/mango/Liquidations.tsx new file mode 100644 index 00000000..2882d9fa --- /dev/null +++ b/components/stats/mango/Liquidations.tsx @@ -0,0 +1,24 @@ +import LiquidationsAtRiskChart from './LiquidationsAtRiskChart' +import PerpLiquidationsChart from './PerpLiquidationsChart' +import TokenLiquidationsChart from './TokenLiquidationsChart' + +const Liquidations = () => { + return ( + <> +

Liquidations

+
+
+ +
+
+ +
+
+ +
+
+ + ) +} + +export default Liquidations diff --git a/components/stats/mango/LiquidationsCharts.tsx b/components/stats/mango/LiquidationsCharts.tsx deleted file mode 100644 index cda98106..00000000 --- a/components/stats/mango/LiquidationsCharts.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import LiquidationsAtRiskChart from './LiquidationsAtRiskChart' -import PerpLiquidationsChart from './PerpLiquidationsChart' -import TokenLiquidationsChart from './TokenLiquidationsChart' - -const LiquidationsCharts = () => { - return ( - <> -
- -
-
- -
-
- -
- - ) -} - -export default LiquidationsCharts diff --git a/components/stats/mango/MangoPerpStatsCharts.tsx b/components/stats/mango/MangoPerpStatsCharts.tsx deleted file mode 100644 index 8870228d..00000000 --- a/components/stats/mango/MangoPerpStatsCharts.tsx +++ /dev/null @@ -1,264 +0,0 @@ -import { useTranslation } from 'next-i18next' -import { useEffect, useMemo, useState } from 'react' -import mangoStore from '@store/mangoStore' -import { PerpStatsItem } from 'types' -import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart' -import { formatYAxis } from 'utils/formatting' -import Switch from '@components/forms/Switch' - -interface ValueItem { - date: string - value: number -} - -interface PerpStatsData { - feeValues: ValueItem[] - openInterestValues: ValueItem[] - volumeValues: ValueItem[] -} - -interface GroupedDataItem extends ValueItem { - intervalStartMillis: number -} - -const groupByHourlyInterval = ( - data: ValueItem[], - intervalDurationHours: number, -) => { - const intervalMillis = intervalDurationHours * 60 * 60 * 1000 - const groupedData = [] - let currentGroup: GroupedDataItem | null = null - for (let i = 0; i < data.length; i++) { - const obj = data[i] - const date = new Date(obj.date) - const intervalStartMillis = - Math.floor(date.getTime() / intervalMillis) * intervalMillis - if ( - !currentGroup || - currentGroup.intervalStartMillis !== intervalStartMillis - ) { - currentGroup = { - ...obj, - intervalStartMillis: intervalStartMillis, - } - groupedData.push(currentGroup) - } else { - currentGroup.value += obj.value - } - } - return groupedData -} - -const MangoPerpStatsCharts = () => { - const { t } = useTranslation(['common', 'stats', 'token', 'trade']) - const loadingPerpStats = mangoStore((s) => s.perpStats.loading) - const perpStats = mangoStore((s) => s.perpStats.data) - const [feesDaysToShow, setFeesDaysToShow] = useState('30') - const [oiDaysToShow, setOiDaysToShow] = useState('30') - const [volumeDaysToShow, setVolumeDaysToShow] = useState('30') - const [showCumulativeFees, setShowCumulativeFees] = useState(true) - const [showCumulativeVolume, setShowCumulativeVolume] = useState(true) - - useEffect(() => { - if (!perpStats || !perpStats.length) { - const actions = mangoStore.getState().actions - actions.fetchPerpStats() - } - }, [perpStats]) - - const [feeValues, openInterestValues, volumeValues] = useMemo(() => { - if (!perpStats || !perpStats.length) return [[], [], []] - const data = perpStats.reduce( - (a: PerpStatsData, c: PerpStatsItem) => { - const hasDateFee = a.feeValues.find( - (d: ValueItem) => d.date === c.date_hour, - ) - - const hasDateOpenInterest = a.openInterestValues.find( - (d: ValueItem) => d.date === c.date_hour, - ) - - const hasDateVolume = a.volumeValues.find( - (d: ValueItem) => d.date === c.date_hour, - ) - - if (!hasDateFee) { - a.feeValues.push({ - date: c.date_hour, - value: c.total_fees, - }) - } else { - hasDateFee.value += c.total_fees - } - - if (!hasDateOpenInterest) { - a.openInterestValues.push({ - date: c.date_hour, - value: Math.floor(c.open_interest * c.price), - }) - } else { - hasDateOpenInterest.value += Math.floor(c.open_interest * c.price) - } - - if (!hasDateVolume) { - a.volumeValues.push({ - date: c.date_hour, - value: c.cumulative_quote_volume, - }) - } else { - hasDateVolume.value += c.cumulative_quote_volume - } - - return a - }, - { feeValues: [], openInterestValues: [], volumeValues: [] }, - ) - - const { feeValues, openInterestValues, volumeValues } = data - - const sortedFeeValues = feeValues.sort( - (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), - ) - const sortedOpenInterestValues = openInterestValues.sort( - (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), - ) - const sortedVolumeValues = volumeValues.sort( - (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), - ) - - let feeChartData = sortedFeeValues - if (!showCumulativeFees) { - const transformedData = [] - for (let i = 1; i < sortedFeeValues.length; i++) { - const currentInterval = { ...sortedFeeValues[i] } - const previousInterval = sortedFeeValues[i - 1] - - // Calculate the absolute fees for the current interval - currentInterval.value = currentInterval.value - previousInterval.value - - transformedData.push(currentInterval) - } - transformedData.unshift(sortedFeeValues[0]) - - if (feesDaysToShow === '30') { - feeChartData = groupByHourlyInterval(transformedData, 24) - } else if (feesDaysToShow === '7') { - feeChartData = groupByHourlyInterval(transformedData, 4) - } else feeChartData = transformedData - } - - let volumeChartData = sortedVolumeValues - if (!showCumulativeVolume) { - const transformedData = [] - for (let i = 1; i < sortedVolumeValues.length; i++) { - const currentInterval = { ...sortedVolumeValues[i] } - const previousInterval = sortedVolumeValues[i - 1] - - // Calculate the absolute fees for the current interval - currentInterval.value = currentInterval.value - previousInterval.value - - transformedData.push(currentInterval) - } - transformedData.unshift(sortedVolumeValues[0]) - - if (volumeDaysToShow === '30') { - volumeChartData = groupByHourlyInterval(transformedData, 24) - } else if (volumeDaysToShow === '7') { - volumeChartData = groupByHourlyInterval(transformedData, 4) - } else volumeChartData = transformedData - } - - return [feeChartData, sortedOpenInterestValues, volumeChartData] - }, [ - feesDaysToShow, - perpStats, - showCumulativeFees, - showCumulativeVolume, - volumeDaysToShow, - ]) - - return ( - <> - {feeValues.length ? ( -
-
- `$${formatYAxis(x)}`} - title="Perp Fees" - xKey="date" - yKey="value" - chartType={showCumulativeFees ? 'area' : 'bar'} - /> -
-
- setShowCumulativeFees(!showCumulativeFees)} - small - > - {t('stats:show-cumulative')} - -
-
- ) : null} - {openInterestValues.length ? ( -
- `$${formatYAxis(x)}`} - title={t('stats:perp-open-interest')} - xKey="date" - yKey="value" - /> -
- ) : null} - {volumeValues.length ? ( -
-
- `$${formatYAxis(x)}`} - title={t('stats:perp-volume')} - xKey="date" - yKey="value" - chartType={showCumulativeVolume ? 'area' : 'bar'} - /> -
-
- setShowCumulativeVolume(!showCumulativeVolume)} - small - > - {t('stats:show-cumulative')} - -
-
- ) : null} - - ) -} - -export default MangoPerpStatsCharts diff --git a/components/stats/mango/MangoStats.tsx b/components/stats/mango/MangoStats.tsx index bd447e34..c9a285db 100644 --- a/components/stats/mango/MangoStats.tsx +++ b/components/stats/mango/MangoStats.tsx @@ -1,14 +1,16 @@ -import LiquidationsCharts from './LiquidationsCharts' -import MangoPerpStatsCharts from './MangoPerpStatsCharts' -import TokenStatsCharts from './TokenStatsCharts' +import Liquidations from './Liquidations' +import DepositsAndBorrows from './DepositsAndBorrows' +import Fees from './Fees' +import Volume from './Volume' const MangoStats = () => { return ( -
- - - -
+ <> + + + + + ) } diff --git a/components/stats/mango/OpenInterest.tsx b/components/stats/mango/OpenInterest.tsx new file mode 100644 index 00000000..bad64980 --- /dev/null +++ b/components/stats/mango/OpenInterest.tsx @@ -0,0 +1,39 @@ +import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart' +import mangoStore from '@store/mangoStore' +import usePerpStatsChartData from 'hooks/usePerpStatsChartData' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { formatYAxis } from 'utils/formatting' + +const OpenInterest = () => { + const { t } = useTranslation(['common', 'token', 'trade']) + const [oiDaysToShow, setOiDaysToShow] = useState('30') + const loadingPerpStats = mangoStore((s) => s.perpStats.loading) + const { openInterestValues } = usePerpStatsChartData() + + return ( + <> +

Open Interest

+
+
+ `$${formatYAxis(x)}`} + title={t('stats:perp-open-interest')} + xKey="date" + yKey="value" + /> +
+
+ + ) +} + +export default OpenInterest diff --git a/components/stats/mango/SwapVolumeChart.tsx b/components/stats/mango/SwapVolumeChart.tsx new file mode 100644 index 00000000..7b671dcb --- /dev/null +++ b/components/stats/mango/SwapVolumeChart.tsx @@ -0,0 +1,30 @@ +import { useQuery } from '@tanstack/react-query' +import { MANGO_DATA_API_URL } from 'utils/constants' + +export const fetchSwapVolume = async () => { + try { + const response = await fetch( + `${MANGO_DATA_API_URL}/stats/mango-swap-volume`, + ) + const data = await response.json() + return data + } catch (e) { + console.log('Failed to fetch swap volume', e) + return [] + } +} + +const SwapVolumeChart = () => { + const { data } = useQuery(['mango-swap-volume'], () => fetchSwapVolume(), { + cacheTime: 1000 * 60 * 10, + staleTime: 1000 * 60, + retry: 3, + refetchOnWindowFocus: false, + }) + + console.log(data) + + return
+} + +export default SwapVolumeChart diff --git a/components/stats/mango/TokenStatsCharts.tsx b/components/stats/mango/TokenStatsCharts.tsx deleted file mode 100644 index 7d5e6e5f..00000000 --- a/components/stats/mango/TokenStatsCharts.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import mangoStore from '@store/mangoStore' -import { useTranslation } from 'next-i18next' -import { useEffect, useMemo, useState } from 'react' -import { formatYAxis } from 'utils/formatting' -import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart' -import { MangoTokenStatsItem } from 'types' -import Switch from '@components/forms/Switch' -import NetDepositsChart from './NetDepositsChart' - -interface GroupedDataItem extends MangoTokenStatsItem { - intervalStartMillis: number -} - -const groupByHourlyInterval = ( - data: MangoTokenStatsItem[], - intervalDurationHours: number, -) => { - const intervalMillis = intervalDurationHours * 60 * 60 * 1000 - const groupedData = [] - let currentGroup: GroupedDataItem | null = null - for (let i = 0; i < data.length; i++) { - const obj = data[i] - const date = new Date(obj.date) - const intervalStartMillis = - Math.floor(date.getTime() / intervalMillis) * intervalMillis - if ( - !currentGroup || - currentGroup.intervalStartMillis !== intervalStartMillis - ) { - currentGroup = { - ...obj, - intervalStartMillis: intervalStartMillis, - } - groupedData.push(currentGroup) - } else { - currentGroup.feesCollected += obj.feesCollected - } - } - return groupedData -} - -const TokenStatsCharts = () => { - const { t } = useTranslation(['common', 'token', 'trade']) - const mangoStats = mangoStore((s) => s.tokenStats.mangoStats) - const loadingStats = mangoStore((s) => s.tokenStats.loading) - const [borrowDaysToShow, setBorrowDaysToShow] = useState('30') - const [depositDaysToShow, setDepositDaysToShow] = useState('30') - const [feesDaysToShow, setFeesDaysToShow] = useState('30') - const [showCumulativeFees, setShowCumulativeFees] = useState(true) - const tokenStatsInitialLoad = mangoStore((s) => s.tokenStats.initialLoad) - - useEffect(() => { - if (!tokenStatsInitialLoad) { - const actions = mangoStore.getState().actions - actions.fetchTokenStats() - } - }, [tokenStatsInitialLoad]) - - const tokenFeesChartData = useMemo(() => { - if (!mangoStats.length) return [] - if (showCumulativeFees) { - return mangoStats - } else { - const transformedData = [] - for (let i = 1; i < mangoStats.length; i++) { - const currentInterval = { ...mangoStats[i] } - const previousInterval = mangoStats[i - 1] - - // Calculate the absolute fees for the current interval - currentInterval.feesCollected = - currentInterval.feesCollected - previousInterval.feesCollected - - transformedData.push(currentInterval) - } - transformedData.unshift(mangoStats[0]) - - if (feesDaysToShow === '30') { - return groupByHourlyInterval(transformedData, 24) - } else if (feesDaysToShow === '7') { - return groupByHourlyInterval(transformedData, 4) - } else return transformedData - } - }, [mangoStats, feesDaysToShow, showCumulativeFees]) - - return ( - <> -
- `$${formatYAxis(x)}`} - title={t('total-deposit-value')} - xKey="date" - yKey={'depositValue'} - /> -
-
- `$${formatYAxis(x)}`} - title={t('total-borrow-value')} - xKey="date" - yKey={'borrowValue'} - /> -
-
- -
-
-
- `$${formatYAxis(x)}`} - title={t('token:token-fees-collected')} - tooltipContent={t('token:tooltip-token-fees-collected')} - xKey="date" - yKey={'feesCollected'} - chartType={showCumulativeFees ? 'area' : 'bar'} - /> -
-
- setShowCumulativeFees(!showCumulativeFees)} - small - > - {t('stats:show-cumulative')} - -
-
- - ) -} - -export default TokenStatsCharts diff --git a/components/stats/mango/Volume.tsx b/components/stats/mango/Volume.tsx new file mode 100644 index 00000000..7bfd0fc4 --- /dev/null +++ b/components/stats/mango/Volume.tsx @@ -0,0 +1,121 @@ +import { useTranslation } from 'next-i18next' +import { useMemo, useState } from 'react' +import mangoStore from '@store/mangoStore' +import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart' +import { formatYAxis } from 'utils/formatting' +import Switch from '@components/forms/Switch' +import usePerpStatsChartData from 'hooks/usePerpStatsChartData' +import SwapVolumeChart from './SwapVolumeChart' + +export interface PerpValueItem { + date: string + value: number +} + +interface GroupedDataPerpItem extends PerpValueItem { + intervalStartMillis: number +} + +export const groupPerpByHourlyInterval = ( + data: PerpValueItem[], + intervalDurationHours: number, +) => { + const intervalMillis = intervalDurationHours * 60 * 60 * 1000 + const groupedData = [] + let currentGroup: GroupedDataPerpItem | null = null + for (let i = 0; i < data.length; i++) { + const obj = data[i] + const date = new Date(obj.date) + const intervalStartMillis = + Math.floor(date.getTime() / intervalMillis) * intervalMillis + if ( + !currentGroup || + currentGroup.intervalStartMillis !== intervalStartMillis + ) { + currentGroup = { + ...obj, + intervalStartMillis: intervalStartMillis, + } + groupedData.push(currentGroup) + } else { + currentGroup.value += obj.value + } + } + return groupedData +} + +const Volume = () => { + const { t } = useTranslation(['common', 'stats', 'token', 'trade']) + const loadingPerpStats = mangoStore((s) => s.perpStats.loading) + const [perpVolumeDaysToShow, setPerpPerpVolumeDaysToShow] = useState('30') + const [showCumulativePerpVolume, setShowCumulativePerpVolume] = useState(true) + const { volumeValues: perpVolumeChartData } = usePerpStatsChartData() + + const perpVolumeValues = useMemo(() => { + if (!perpVolumeChartData || !perpVolumeChartData.length) return [] + + let volumeChartData = perpVolumeChartData + if (!showCumulativePerpVolume) { + const transformedData = [] + for (let i = 1; i < perpVolumeChartData.length; i++) { + const currentInterval = { ...perpVolumeChartData[i] } + const previousInterval = perpVolumeChartData[i - 1] + + // Calculate the absolute fees for the current interval + currentInterval.value = currentInterval.value - previousInterval.value + + transformedData.push(currentInterval) + } + transformedData.unshift(perpVolumeChartData[0]) + + if (perpVolumeDaysToShow === '30') { + volumeChartData = groupPerpByHourlyInterval(transformedData, 24) + } else if (perpVolumeDaysToShow === '7') { + volumeChartData = groupPerpByHourlyInterval(transformedData, 4) + } else volumeChartData = transformedData + } + + return volumeChartData + }, [perpVolumeDaysToShow, perpVolumeChartData, showCumulativePerpVolume]) + + return ( + <> +

Volume

+
+
+
+ `$${formatYAxis(x)}`} + title={t('stats:perp-volume')} + xKey="date" + yKey="value" + chartType={showCumulativePerpVolume ? 'area' : 'bar'} + /> +
+
+ + setShowCumulativePerpVolume(!showCumulativePerpVolume) + } + small + > + {t('stats:show-cumulative')} + +
+
+ +
+ + ) +} + +export default Volume diff --git a/hooks/usePerpStatsChartData.ts b/hooks/usePerpStatsChartData.ts new file mode 100644 index 00000000..140a9b2f --- /dev/null +++ b/hooks/usePerpStatsChartData.ts @@ -0,0 +1,90 @@ +import mangoStore from '@store/mangoStore' +import { useEffect, useMemo } from 'react' +import { PerpStatsItem } from 'types' + +export interface PerpValueItem { + date: string + value: number +} + +interface PerpStatsData { + feeValues: PerpValueItem[] + openInterestValues: PerpValueItem[] + volumeValues: PerpValueItem[] +} + +export default function usePerpStatsChartData() { + const perpStats = mangoStore((s) => s.perpStats.data) + + useEffect(() => { + if (!perpStats || !perpStats.length) { + const actions = mangoStore.getState().actions + actions.fetchPerpStats() + } + }, [perpStats]) + + const [feeValues, openInterestValues, volumeValues] = useMemo(() => { + if (!perpStats || !perpStats.length) return [[], [], []] + const data = perpStats.reduce( + (a: PerpStatsData, c: PerpStatsItem) => { + const hasDateFee = a.feeValues.find( + (d: PerpValueItem) => d.date === c.date_hour, + ) + + const hasDateOpenInterest = a.openInterestValues.find( + (d: PerpValueItem) => d.date === c.date_hour, + ) + + const hasDateVolume = a.volumeValues.find( + (d: PerpValueItem) => d.date === c.date_hour, + ) + + if (!hasDateFee) { + a.feeValues.push({ + date: c.date_hour, + value: c.total_fees, + }) + } else { + hasDateFee.value += c.total_fees + } + + if (!hasDateOpenInterest) { + a.openInterestValues.push({ + date: c.date_hour, + value: Math.floor(c.open_interest * c.price), + }) + } else { + hasDateOpenInterest.value += Math.floor(c.open_interest * c.price) + } + + if (!hasDateVolume) { + a.volumeValues.push({ + date: c.date_hour, + value: c.cumulative_quote_volume, + }) + } else { + hasDateVolume.value += c.cumulative_quote_volume + } + + return a + }, + { feeValues: [], openInterestValues: [], volumeValues: [] }, + ) + + const { feeValues, openInterestValues, volumeValues } = data + + const sortedFeeValues = feeValues.sort( + (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), + ) + const sortedOpenInterestValues = openInterestValues.sort( + (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), + ) + const sortedVolumeValues = volumeValues.sort( + (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), + ) + + return [sortedFeeValues, sortedOpenInterestValues, sortedVolumeValues] + }, [perpStats]) + + return { feeValues, openInterestValues, volumeValues } +}