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 } from '../utils' import { PerpMarket } from '@blockworks-foundation/mango-client' function calculateFundingRate(perpStats, perpMarket, oraclePrice) { const oldestStat = perpStats[perpStats.length - 1] const latestStat = perpStats[0] if (!latestStat || !perpMarket) return null // 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) // TODO - use avgPrice and discard oraclePrice once stats are better // const avgPrice = (latestStat.baseOraclePrice + oldestStat.baseOraclePrice) / 2 const basePriceInBaseLots = oraclePrice * perpMarket.baseLotsToNumber(1) return (fundingInQuoteDecimals / basePriceInBaseLots) * 100 } function parseOpenInterest(perpMarket: PerpMarket) { if (!perpMarket) return 0 return perpMarket.baseLotsToNumber(perpMarket.openInterest) / 2 } const MarketHeader = () => { 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 [ohlcv, setOhlcv] = useState(null) const [loading, setLoading] = useState(false) const [perpStats, setPerpStats] = useState([]) const [spotStats, setSpotStats] = useState(null) // const change = ohlcv ? ((ohlcv.c[0] - ohlcv.o[0]) / ohlcv.o[0]) * 100 : '--' const volume = ohlcv ? ohlcv.v[0] : '--' const fetchSpotStats = useCallback(async () => { const urlParams = new URLSearchParams({ mangoGroup: groupConfig.name }) urlParams.append('market', selectedMarketName) const spotStats = await fetch( 'https://mango-stats-v3.herokuapp.com/spot/change/24?' + urlParams ) const parsedSpotStats = await spotStats.json() setSpotStats(parsedSpotStats) }, [selectedMarketName, groupConfig]) const fetchPerpStats = useCallback(async () => { const perpStats = await fetch( `https://mango-stats-v3.herokuapp.com/perp/funding_rate?market=${selectedMarketName}` ) const parsedPerpStats = await perpStats.json() setPerpStats(parsedPerpStats) }, [selectedMarketName]) useEffect(() => { if (isPerpMarket) { fetchPerpStats() } else { fetchSpotStats() } }, [marketConfig]) 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 (
{baseSymbol}
{isPerpMarket ? '-' : '/'}
{isPerpMarket ? 'PERP' : groupConfig.quoteSymbol}
Oracle price
{oraclePrice ? formatUsdValue(oraclePrice) : '--'}
24h Change
{spotStats?.change ? (
0 ? `text-th-green` : spotStats.change < 0 ? `text-th-red` : `text-th-fgd-1` }`} > {spotStats.change.toFixed(2) + '%'}
) : ( )}
24h Vol
{ohlcv && !loading && volume ? ( volume !== '--' ? ( <> {volume.toFixed(2)} {baseSymbol} ) : ( volume ) ) : ( )}
{isPerpMarket ? ( <>
Avg Funding Rate (1h)
{calculateFundingRate( perpStats, selectedMarket, oraclePrice ) ? ( `${calculateFundingRate( perpStats, selectedMarket, oraclePrice )?.toFixed(4)}%` ) : ( )}
Open Interest
{selectedMarket ? ( `${parseOpenInterest( selectedMarket as PerpMarket )} ${baseSymbol}` ) : ( )}
) : null}
{connected && mangoAccount ? : null}
) } export default MarketHeader export const MarketDataLoader = () => (
)