import React, { useCallback, useMemo, useState } from 'react' import useMangoStore from '../stores/useMangoStore' import usePrevious from '../hooks/usePrevious' import useInterval from '../hooks/useInterval' import ChartApi from '../utils/chartDataConnector' import UiLock from './UiLock' import ManualRefresh from './ManualRefresh' import useOraclePrice from '../hooks/useOraclePrice' import DayHighLow from './DayHighLow' import { useEffect } from 'react' import { formatUsdValue, usdFormatter } from '../utils' import { PerpMarket } from '@blockworks-foundation/mango-client' import BN from 'bn.js' import { useViewport } from '../hooks/useViewport' import { breakpoints } from './TradePageGrid' const SECONDS = 1000 function calculateFundingRate(perpStats, perpMarket) { const oldestStat = perpStats[perpStats.length - 1] const latestStat = perpStats[0] if (!latestStat || !(perpMarket instanceof PerpMarket)) return 0.0 // Averaging long and short funding excludes socialized loss const startFunding = (parseFloat(oldestStat.longFunding) + parseFloat(oldestStat.shortFunding)) / 2 const endFunding = (parseFloat(latestStat.longFunding) + parseFloat(latestStat.shortFunding)) / 2 const fundingDifference = endFunding - startFunding const fundingInQuoteDecimals = fundingDifference / Math.pow(10, perpMarket.quoteDecimals) // const avgPrice = // (parseFloat(latestStat.baseOraclePrice) + // parseFloat(oldestStat.baseOraclePrice)) / // 2 const basePriceInBaseLots = parseFloat(latestStat.baseOraclePrice) * perpMarket.baseLotsToNumber(new BN(1)) return (fundingInQuoteDecimals / basePriceInBaseLots) * 100 } function parseOpenInterest(perpMarket: PerpMarket) { if (!(perpMarket instanceof PerpMarket)) return 0 return perpMarket.baseLotsToNumber(perpMarket.openInterest) / 2 } const MarketDetails = () => { const oraclePrice = useOraclePrice() const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config) const marketConfig = useMangoStore((s) => s.selectedMarket.config) const selectedMarket = useMangoStore((s) => s.selectedMarket.current) const baseSymbol = marketConfig.baseSymbol const selectedMarketName = marketConfig.name const isPerpMarket = marketConfig.kind === 'perp' const previousMarketName: string = usePrevious(selectedMarketName) const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const connected = useMangoStore((s) => s.wallet.connected) const { width } = useViewport() const isMobile = width ? width < breakpoints.sm : false const [ohlcv, setOhlcv] = useState(null) const [, setLoading] = useState(false) const [perpStats, setPerpStats] = useState([]) const [perpVolume, setPerpVolume] = useState(0) const change = ohlcv ? ((ohlcv.c[0] - ohlcv.o[0]) / ohlcv.o[0]) * 100 : '' // const volume = ohlcv ? ohlcv.v[0] : '--' const fetchPerpStats = useCallback(async () => { const urlParams = new URLSearchParams({ mangoGroup: groupConfig.name }) urlParams.append('market', selectedMarketName) const perpStats = await fetch( `https://mango-stats-v3.herokuapp.com/perp/funding_rate?` + urlParams ) const parsedPerpStats = await perpStats.json() const perpVolume = await fetch( `https://event-history-api.herokuapp.com/stats/perps/${marketConfig.publicKey.toString()}` ) const parsedPerpVolume = await perpVolume.json() setPerpVolume(parsedPerpVolume?.data?.volume) setPerpStats(parsedPerpStats) }, [selectedMarketName]) useInterval(() => { if (isPerpMarket) { fetchPerpStats() } }, 120 * SECONDS) useEffect(() => { if (isPerpMarket) { fetchPerpStats() } }, [isPerpMarket, fetchPerpStats]) const fetchOhlcv = useCallback(async () => { if (!selectedMarketName) return // calculate from and to date (0:00UTC to 23:59:59UTC) const date = new Date() const utcFrom = new Date( Date.UTC( date.getFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0 ) ) const utcTo = new Date( Date.UTC( date.getFullYear(), date.getUTCMonth(), date.getUTCDate(), 23, 59, 59 ) ) const from = utcFrom.getTime() / 1000 const to = utcTo.getTime() / 1000 const ohlcv = await ChartApi.getOhlcv(selectedMarketName, '1D', from, to) if (ohlcv) { setOhlcv(ohlcv) setLoading(false) } }, [selectedMarketName]) useInterval(async () => { fetchOhlcv() }, 5000) useMemo(() => { if (previousMarketName !== selectedMarketName) { setLoading(true) } }, [selectedMarketName]) return (