import { PerpMarket } from '@blockworks-foundation/mango-client' import { useState, useMemo } from 'react' import useMangoStore from '../../stores/useMangoStore' import Chart from '../Chart' import BN from 'bn.js' import { perpContractPrecision } from '../../utils' import { useTranslation } from 'next-i18next' import Select from '../Select' import { marketsSelector } from '../../stores/selectors' import dayjs from 'dayjs' function calculateFundingRate( oldestLongFunding, oldestShortFunding, latestLongFunding, latestShortFunding, perpMarket: PerpMarket, oraclePrice ) { if (!perpMarket || !oraclePrice) return 0.0 // Averaging long and short funding excludes socialized loss const startFunding = (parseFloat(oldestLongFunding) + parseFloat(oldestShortFunding)) / 2 const endFunding = (parseFloat(latestLongFunding) + parseFloat(latestShortFunding)) / 2 const fundingDifference = endFunding - startFunding const fundingInQuoteDecimals = fundingDifference / Math.pow(10, perpMarket.quoteDecimals) // TODO - use avgPrice and discard oraclePrice once stats are better // const avgPrice = (latestStat.baseOraclePrice + oldestStat.baseOraclePrice) / 2 const basePriceInBaseLots = oraclePrice * perpMarket.baseLotsToNumber(new BN(1)) return (fundingInQuoteDecimals / basePriceInBaseLots) * 100 } export default function StatsPerps({ perpStats }) { const { t } = useTranslation('common') const [selectedAsset, setSelectedAsset] = useState('BTC-PERP') const marketConfigs = useMangoStore( (s) => s.selectedMangoGroup.config ).perpMarkets const selectedMarketConfig = marketConfigs?.find( (m) => m.name === selectedAsset ) const marketDirectory = useMangoStore(marketsSelector) const markets = Object.values(marketDirectory) const perpMarkets = useMemo(() => { return markets.filter((m) => m instanceof PerpMarket) as PerpMarket[] }, [markets]) const selectedMarket = useMemo(() => { if (selectedMarketConfig) { return perpMarkets.find((m) => m.publicKey.equals(selectedMarketConfig.publicKey) ) } }, [selectedMarketConfig, perpMarkets]) const perpsData = useMemo(() => { if (perpStats.length === 0 || !selectedMarket) return [] let selectedStatsData = perpStats.filter( (stat) => stat.name === selectedAsset ) if (selectedAsset == 'SOL-PERP') { const startTimestamp = 1632160800000 selectedStatsData = selectedStatsData.filter( (stat) => new Date(stat.hourly).getTime() >= startTimestamp ) } const perpsData = selectedStatsData.map((x) => { return { fundingRate: calculateFundingRate( x.oldestLongFunding, x.oldestShortFunding, x.latestLongFunding, x.latestShortFunding, selectedMarket, x.baseOraclePrice ), openInterest: selectedMarket.baseLotsToNumber(x.openInterest) / 2, time: x.hourly, } }) if (selectedAsset === 'BTC-PERP') { const index = perpsData.findIndex( (x) => x.time === '2021-09-15T05:00:00.000Z' ) perpsData.splice(index, 1) } return perpsData }, [selectedAsset, perpStats, selectedMarket]) if (!selectedMarket) return null const progress = 1 - selectedMarket.liquidityMiningInfo.mngoLeft.toNumber() / selectedMarket.liquidityMiningInfo.mngoPerPeriod.toNumber() const start = selectedMarket.liquidityMiningInfo.periodStart.toNumber() const now = Date.now() / 1000 const elapsed = now - start const est = start + elapsed / progress const lmi = selectedMarket.liquidityMiningInfo const maxDepthUi = (lmi.maxDepthBps.toNumber() * selectedMarket.baseLotSize.toNumber()) / Math.pow(10, selectedMarket.baseDecimals) return ( <>
{marketConfigs?.map((market, index) => (
0 ? 'ml-4 md:ml-2' : null } default-transition cursor-pointer rounded-md ${ selectedAsset === market.name ? `text-th-primary` : `text-th-fgd-3 hover:text-th-fgd-1` } `} onClick={() => setSelectedAsset(market.name)} key={market.name as string} > {market.baseSymbol}
))}

{selectedAsset.split(/-|\//)[0]} {t('perpetual-futures')}

`${x.toFixed(4)}%`} tickFormat={(x) => x.toLocaleString(undefined, { maximumFractionDigits: 4 }) } type="area" yAxisWidth={70} />
{selectedMarketConfig?.baseSymbol ? ( x && x.toLocaleString(undefined, { maximumFractionDigits: perpContractPrecision[selectedMarketConfig.baseSymbol], }) + ' ' + selectedMarketConfig.baseSymbol } type="area" /> ) : null}

{t('liquidity-mining')}

{t('depth-rewarded')}

{maxDepthUi.toLocaleString() + ' '} {selectedMarketConfig?.baseSymbol ? ( {selectedMarketConfig.baseSymbol} ) : null}

{t('target-period-length')}

{( selectedMarket.liquidityMiningInfo.targetPeriodLength.toNumber() / 60 ).toFixed()}{' '} {t('minutes')}

{t('mngo-per-period')}

{( selectedMarket.liquidityMiningInfo.mngoPerPeriod.toNumber() / Math.pow(10, 6) ).toFixed(2)}

{t('mngo-left-period')}

{( selectedMarket.liquidityMiningInfo.mngoLeft.toNumber() / Math.pow(10, 6) ).toFixed(2)}

{t('est-period-end')}

{dayjs(est * 1000).format('DD MMM YYYY')}
{dayjs(est * 1000).format('h:mma')}

{t('period-progress')}

{(progress * 100).toFixed(2)}%
) }