mango-ui-v3/components/MarketHeader.tsx

255 lines
8.2 KiB
TypeScript
Raw Normal View History

import React, { useCallback, useMemo, useState } from 'react'
2021-04-19 06:45:59 -07:00
import useMangoStore from '../stores/useMangoStore'
import usePrevious from '../hooks/usePrevious'
import useInterval from '../hooks/useInterval'
import ChartApi from '../utils/chartDataConnector'
2021-04-19 06:45:59 -07:00
import UiLock from './UiLock'
import ManualRefresh from './ManualRefresh'
2021-07-05 08:03:57 -07:00
import useOraclePrice from '../hooks/useOraclePrice'
import DayHighLow from './DayHighLow'
2021-08-13 11:54:09 -07:00
import { useEffect } from 'react'
2021-08-15 06:31:59 -07:00
import { formatUsdValue } from '../utils'
import { PerpMarket } from '@blockworks-foundation/mango-client'
import BN from 'bn.js'
2021-08-13 11:54:09 -07:00
2021-08-31 13:31:35 -07:00
const SECONDS = 1000
function calculateFundingRate(perpStats, perpMarket) {
2021-08-13 11:54:09 -07:00
const oldestStat = perpStats[perpStats.length - 1]
const latestStat = perpStats[0]
if (!latestStat || !(perpMarket instanceof PerpMarket)) return 0.0
2021-08-13 11:54:09 -07:00
// 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
2021-08-13 11:54:09 -07:00
const fundingInQuoteDecimals =
fundingDifference / Math.pow(10, perpMarket.quoteDecimals)
const avgPrice =
(parseFloat(latestStat.baseOraclePrice) +
parseFloat(oldestStat.baseOraclePrice)) /
2
const basePriceInBaseLots = avgPrice * perpMarket.baseLotsToNumber(new BN(1))
2021-08-13 11:54:09 -07:00
return (fundingInQuoteDecimals / basePriceInBaseLots) * 100
}
function parseOpenInterest(perpMarket: PerpMarket) {
if (!(perpMarket instanceof PerpMarket)) return 0
2021-08-13 12:06:17 -07:00
return perpMarket.baseLotsToNumber(perpMarket.openInterest) / 2
2021-08-13 11:54:09 -07:00
}
2021-04-19 06:45:59 -07:00
const MarketHeader = () => {
2021-07-05 08:03:57 -07:00
const oraclePrice = useOraclePrice()
2021-08-20 05:10:59 -07:00
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
2021-08-13 11:54:09 -07:00
const selectedMarket = useMangoStore((s) => s.selectedMarket.current)
2021-06-23 08:32:33 -07:00
const baseSymbol = marketConfig.baseSymbol
const selectedMarketName = marketConfig.name
2021-08-20 05:10:59 -07:00
const isPerpMarket = marketConfig.kind === 'perp'
const previousMarketName: string = usePrevious(selectedMarketName)
2021-06-23 08:32:33 -07:00
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const connected = useMangoStore((s) => s.wallet.connected)
const [ohlcv, setOhlcv] = useState(null)
const [, setLoading] = useState(false)
2021-08-13 11:54:09 -07:00
const [perpStats, setPerpStats] = useState([])
const change = ohlcv ? ((ohlcv.c[0] - ohlcv.o[0]) / ohlcv.o[0]) * 100 : ''
// const volume = ohlcv ? ohlcv.v[0] : '--'
2021-08-13 14:30:39 -07:00
const fetchPerpStats = useCallback(async () => {
2021-08-25 10:40:32 -07:00
const urlParams = new URLSearchParams({ mangoGroup: groupConfig.name })
urlParams.append('market', selectedMarketName)
2021-08-13 11:54:09 -07:00
const perpStats = await fetch(
2021-08-25 10:40:32 -07:00
`https://mango-stats-v3.herokuapp.com/perp/funding_rate?` + urlParams
2021-08-13 11:54:09 -07:00
)
const parsedPerpStats = await perpStats.json()
setPerpStats(parsedPerpStats)
}, [selectedMarketName])
2021-08-31 13:31:35 -07:00
useInterval(() => {
if (isPerpMarket) {
fetchPerpStats()
}
}, 120 * SECONDS)
2021-08-13 14:30:39 -07:00
useEffect(() => {
2021-08-20 05:10:59 -07:00
if (isPerpMarket) {
2021-08-13 11:54:09 -07:00
fetchPerpStats()
}
}, [isPerpMarket, fetchPerpStats])
2021-08-13 11:54:09 -07:00
const fetchOhlcv = useCallback(async () => {
if (!selectedMarketName) return
// calculate from and to date (0:00UTC to 23:59:59UTC)
const date = new Date()
2021-05-20 04:21:36 -07:00
const utcFrom = new Date(
2021-06-05 11:40:07 -07:00
Date.UTC(
date.getFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
0,
0,
0
)
)
2021-05-20 04:21:36 -07:00
const utcTo = new Date(
2021-06-05 11:40:07 -07:00
Date.UTC(
date.getFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
23,
59,
59
)
)
2021-06-05 11:40:07 -07:00
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])
2021-04-19 06:45:59 -07:00
return (
<div
2021-08-31 05:46:47 -07:00
className={`flex flex-col relative lg:flex-row lg:items-center lg:justify-between pt-4 px-6 lg:pb-1 lg:pt-8 lg:px-6`}
>
2021-08-31 05:46:47 -07:00
<div className="flex flex-col lg:flex-row lg:items-center">
<div className="hidden sm:block sm:pb-4 lg:pb-0 sm:pr-6">
<div className="flex items-center">
<img
alt=""
width="24"
height="24"
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
2021-06-23 06:16:37 -07:00
<div className="font-semibold pr-0.5 text-xl">{baseSymbol}</div>
<span className="text-th-fgd-4 text-xl">
2021-08-20 05:10:59 -07:00
{isPerpMarket ? '-' : '/'}
2021-06-23 06:16:37 -07:00
</span>
<div className="font-semibold pl-0.5 text-xl">
2021-08-20 05:10:59 -07:00
{isPerpMarket ? 'PERP' : groupConfig.quoteSymbol}
</div>
</div>
</div>
2021-08-31 05:46:47 -07:00
<div className="grid grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-flow-col lg:grid-rows-1 lg:gap-6">
<div className="">
2021-08-16 06:31:25 -07:00
<div className="text-th-fgd-3 tiny-text pb-0.5">Oracle price</div>
<div className="font-semibold text-th-fgd-1 text-xs">
2021-08-15 06:31:59 -07:00
{oraclePrice ? formatUsdValue(oraclePrice) : '--'}
</div>
</div>
2021-08-31 05:46:47 -07:00
<div className="">
<div className="text-th-fgd-3 tiny-text pb-0.5">Daily Change</div>
2021-08-28 19:02:22 -07:00
{change || change === 0 ? (
2021-08-13 14:45:22 -07:00
<div
className={`font-semibold text-xs ${
change > 0
2021-08-13 14:45:22 -07:00
? `text-th-green`
: change < 0
2021-08-13 14:45:22 -07:00
? `text-th-red`
: `text-th-fgd-1`
}`}
>
2021-08-28 19:02:22 -07:00
{change.toFixed(2) + '%'}
2021-08-13 14:45:22 -07:00
</div>
) : (
<MarketDataLoader />
)}
</div>
{/* <div className="">
<div className="text-th-fgd-3 tiny-text pb-0.5">Daily Volume</div>
<div className="font-semibold text-th-fgd-1 text-xs">
2021-06-17 17:32:14 -07:00
{ohlcv && !loading && volume ? (
volume !== '--' ? (
<>
{volume.toFixed(2)}
2021-08-16 06:31:25 -07:00
<span className="ml-1 text-th-fgd-3 tiny-text pb-0.5">
2021-07-23 07:07:05 -07:00
{baseSymbol}
</span>
</>
) : (
volume
)
) : (
<MarketDataLoader />
)}
</div>
</div> */}
{isPerpMarket && selectedMarket instanceof PerpMarket ? (
<>
2021-08-31 05:46:47 -07:00
<div className="">
2021-08-16 06:31:25 -07:00
<div className="text-th-fgd-3 tiny-text pb-0.5">
2021-08-13 12:10:50 -07:00
Avg Funding Rate (1h)
</div>
<div className="font-semibold text-th-fgd-1 text-xs">
2021-08-25 10:43:17 -07:00
{selectedMarket ? (
`${calculateFundingRate(perpStats, selectedMarket)?.toFixed(
4
)}%`
2021-08-21 06:02:51 -07:00
) : (
<MarketDataLoader />
)}
</div>
</div>
2021-08-31 05:46:47 -07:00
<div className="">
2021-08-16 06:31:25 -07:00
<div className="text-th-fgd-3 tiny-text pb-0.5">
Open Interest
</div>
2021-08-13 11:54:09 -07:00
<div className="font-semibold text-th-fgd-1 text-xs">
{selectedMarket ? (
`${parseOpenInterest(
selectedMarket as PerpMarket
)} ${baseSymbol}`
2021-08-21 06:02:51 -07:00
) : (
<MarketDataLoader />
)}
2021-08-13 11:54:09 -07:00
</div>
</div>
</>
) : null}
2021-08-25 11:30:16 -07:00
<DayHighLow
high={ohlcv?.h[0]}
low={ohlcv?.l[0]}
latest={oraclePrice?.toNumber()}
/>
</div>
2021-04-19 06:45:59 -07:00
</div>
2021-08-31 05:46:47 -07:00
<div className="absolute right-4 bottom-0 sm:bottom-auto lg:right-6 flex items-center justify-end">
2021-04-19 06:45:59 -07:00
<UiLock />
2021-06-23 08:32:33 -07:00
{connected && mangoAccount ? <ManualRefresh className="pl-2" /> : null}
2021-04-19 06:45:59 -07:00
</div>
</div>
)
}
export default MarketHeader
2021-08-21 06:02:51 -07:00
export const MarketDataLoader = () => (
<div className="animate-pulse bg-th-bkg-3 h-3.5 mt-0.5 w-10 rounded-sm" />
2021-04-19 06:45:59 -07:00
)