import { useState, useEffect } from 'react' import { useTheme } from 'next-themes' import cloneDeep from 'lodash/cloneDeep' import dayjs from 'dayjs' import { AreaChart, Area, XAxis, YAxis, Tooltip as ChartTooltip, } from 'recharts' import { InformationCircleIcon } from '@heroicons/react/outline' import useDimensions from 'react-cool-dimensions' import { useTranslation } from 'next-i18next' import ButtonGroup from '../ButtonGroup' import { formatUsdValue } from '../../utils' import { numberCompacter } from '../SwapTokenInfo' import Checkbox from '../Checkbox' import Tooltip from '../Tooltip' type PerformanceChart = { hourlyPerformanceStats: any[] performanceRange: '24hr' | '7d' | '30d' | '3m' } const defaultData = [ { account_equity: 0, pnl: 0, time: '2022-01-01T00:00:00.000Z' }, { account_equity: 0, pnl: 0, time: '2023-01-01T00:00:00.000Z' }, ] const PerformanceChart = ({ hourlyPerformanceStats, performanceRange, accountValue, }) => { const { theme } = useTheme() const { t } = useTranslation('common') const { observe, width, height } = useDimensions() const [chartData, setChartData] = useState([]) const [mouseData, setMouseData] = useState(null) const [chartToShow, setChartToShow] = useState('Value') const [showSpotPnl, setShowSpotPnl] = useState(true) const [showPerpPnl, setShowPerpPnl] = useState(true) useEffect(() => { if (hourlyPerformanceStats.length > 0) { if (performanceRange === '3m') { setChartData(hourlyPerformanceStats.slice().reverse()) } if (performanceRange === '30d') { const start = new Date( // @ts-ignore dayjs().utc().hour(0).minute(0).subtract(29, 'day') ).getTime() const chartData = cloneDeep(hourlyPerformanceStats).filter( (d) => new Date(d.time).getTime() > start ) const pnlStart = chartData[chartData.length - 1].pnl const perpPnlStart = chartData[chartData.length - 1].perp_pnl for (let i = 0; i < chartData.length; i++) { if (i === chartData.length - 1) { chartData[i].pnl = 0 chartData[i].perp_pnl = 0 } else { chartData[i].pnl = chartData[i].pnl - pnlStart chartData[i].perp_pnl = chartData[i].perp_pnl - perpPnlStart } } setChartData(chartData.reverse()) } if (performanceRange === '7d') { const start = new Date( // @ts-ignore dayjs().utc().hour(0).minute(0).subtract(7, 'day') ).getTime() const chartData = cloneDeep(hourlyPerformanceStats).filter( (d) => new Date(d.time).getTime() > start ) const pnlStart = chartData[chartData.length - 1].pnl const perpPnlStart = chartData[chartData.length - 1].perp_pnl for (let i = 0; i < chartData.length; i++) { if (i === chartData.length - 1) { chartData[i].pnl = 0 chartData[i].perp_pnl = 0 } else { chartData[i].pnl = chartData[i].pnl - pnlStart chartData[i].perp_pnl = chartData[i].perp_pnl - perpPnlStart } } setChartData(chartData.reverse()) } if (performanceRange === '24h') { const start = new Date( // @ts-ignore dayjs().utc().hour(0).minute(0).subtract(1, 'day') ).getTime() const chartData = cloneDeep(hourlyPerformanceStats).filter( (d) => new Date(d.time).getTime() > start ) const pnlStart = chartData[chartData.length - 1].pnl const perpPnlStart = chartData[chartData.length - 1].perp_pnl for (let i = 0; i < chartData.length; i++) { if (i === chartData.length - 1) { chartData[i].pnl = 0 chartData[i].perp_pnl = 0 } else { chartData[i].pnl = chartData[i].pnl - pnlStart chartData[i].perp_pnl = chartData[i].perp_pnl - perpPnlStart } } setChartData(chartData.reverse()) } } else { setChartData([]) } }, [hourlyPerformanceStats, performanceRange]) useEffect(() => { if (chartData.length > 0) { for (const stat of chartData) { stat.spot_pnl = stat.pnl - stat.perp_pnl } } }, [chartData]) const handleMouseMove = (coords) => { if (coords.activePayload) { setMouseData(coords.activePayload[0].payload) } } const handleMouseLeave = () => { setMouseData(null) } const renderPnlChartTitle = () => { if (showPerpPnl && showSpotPnl) { return t('pnl') } if (!showSpotPnl) { return `${t('perp')} ${t('pnl')}` } if (!showPerpPnl) { return `${t('spot')} ${t('pnl')}` } } const formatDateAxis = (date) => { if (['3m', '30d'].includes(performanceRange)) { return dayjs(date).format('D MMM') } else if (performanceRange === '7d') { return dayjs(date).format('ddd, h:mma') } else { return dayjs(date).format('h:mma') } } const pnlChartDataKey = () => { if (!showPerpPnl && showSpotPnl) { return 'spot_pnl' } else if (!showSpotPnl && showPerpPnl) { return 'perp_pnl' } else { return 'pnl' } } const pnlChartColor = chartToShow === 'PnL' && chartData.length > 0 && chartData[chartData.length - 1][pnlChartDataKey()] > 0 ? theme === 'Mango' ? '#AFD803' : '#5EBF4D' : theme === 'Mango' ? '#F84638' : '#CC2929' return (
{chartToShow === 'Value' ? t('account-value') : renderPnlChartTitle()}{' '} {`(${t('timeframe-desc', { timeframe: performanceRange, })})`}
{mouseData ? ( <>
{formatUsdValue( mouseData[ chartToShow === 'PnL' ? pnlChartDataKey() : 'account_equity' ] )}
{dayjs(mouseData['time']).format('ddd MMM D YYYY, h:mma')}
) : chartData.length === 0 ? ( <>
--
{dayjs().format('ddd MMM D YYYY, h:mma')}
) : chartData.length > 0 ? ( <>
{chartToShow === 'PnL' ? formatUsdValue( chartData[chartData.length - 1][pnlChartDataKey()] ) : formatUsdValue(accountValue)}
{chartToShow === 'PnL' ? dayjs(chartData[chartData.length - 1]['time']).format( 'ddd MMM D YYYY, h:mma' ) : dayjs().format('ddd MMM D YYYY, h:mma')}
) : ( <>
)}
setChartToShow(v)} values={['Value', 'PnL']} names={[t('value'), t('pnl')]} />
{chartToShow === 'PnL' ? (
setShowSpotPnl(e.target.checked)} > {t('include-spot')} setShowPerpPnl(e.target.checked)} > {t('include-perp')}
) : null}
{chartData.length > 0 ? ( } /> 0 ? 'url(#greenGradientArea)' : 'url(#redGradientArea)' : 'url(#defaultGradientArea)' } fillOpacity={0.3} /> numberCompacter.format(v)} /> formatDateAxis(v)} /> ) : null}
) } export default PerformanceChart