mango-ui-v3/components/stats-page/StatsPerps.tsx

210 lines
6.0 KiB
TypeScript
Raw Normal View History

import { PerpMarket } from '@blockworks-foundation/mango-client'
2021-07-13 06:52:48 -07:00
import { useState } from 'react'
import useMangoGroupConfig from '../../hooks/useMangoGroupConfig'
import useMangoStore from '../../stores/useMangoStore'
2021-07-13 06:52:48 -07:00
import Chart from '../Chart'
2021-09-06 10:28:35 -07:00
import BN from 'bn.js'
2021-07-13 06:52:48 -07:00
const icons = {
'BTC-PERP': '/assets/icons/btc.svg',
'ETH-PERP': '/assets/icons/eth.svg',
'SOL-PERP': '/assets/icons/sol.svg',
'SRM-PERP': '/assets/icons/srm.svg',
'USDT-PERP': '/assets/icons/usdt.svg',
'MNGO-PERP': '/assets/icons/mngo.svg',
2021-07-13 06:52:48 -07:00
}
function calculateFundingRate(
oldestLongFunding,
oldestShortFunding,
latestLongFunding,
latestShortFunding,
perpMarket,
oraclePrice
) {
if (!perpMarket || !oraclePrice) return 0.0
2021-07-13 06:52:48 -07:00
// 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
2021-09-06 10:28:35 -07:00
const basePriceInBaseLots =
oraclePrice * perpMarket.baseLotsToNumber(new BN(1))
return (fundingInQuoteDecimals / basePriceInBaseLots) * 100
}
export default function StatsPerps({ perpStats }) {
const [selectedAsset, setSelectedAsset] = useState<string>('BTC-PERP')
const marketConfigs = useMangoGroupConfig().perpMarkets
const selectedMarketConfig = marketConfigs.find(
(m) => m.name === selectedAsset
)
const markets = Object.values(
useMangoStore.getState().selectedMangoGroup.markets
).filter((m) => m instanceof PerpMarket) as PerpMarket[]
const selectedMarket = markets.find((m) =>
m.publicKey.equals(selectedMarketConfig.publicKey)
)
let selectedStatsData = perpStats.filter(
(stat) => stat.name === selectedAsset
)
2021-09-06 10:28:35 -07:00
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
),
2021-08-30 17:24:04 -07:00
openInterest: selectedMarket.baseLotsToNumber(x.openInterest) / 2,
time: x.hourly,
}
})
2021-07-13 06:52:48 -07:00
return (
<>
<div className="flex items-center justify-between mb-4 w-full">
2021-07-13 06:52:48 -07:00
<AssetHeader asset={selectedAsset} />
<div className="flex">
{marketConfigs.map((market) => (
2021-07-13 06:52:48 -07:00
<div
className={`bg-th-bkg-3 cursor-pointer default-transition ml-2 px-2 py-1 rounded-md whitespace-nowrap
2021-07-13 06:52:48 -07:00
${
selectedAsset === market.name
2021-07-13 06:52:48 -07:00
? `ring-1 ring-inset ring-th-primary text-th-primary`
: `text-th-fgd-1 opacity-50 hover:opacity-100`
}
`}
onClick={() => setSelectedAsset(market.name)}
key={market.name as string}
2021-07-13 06:52:48 -07:00
>
{market.name}
2021-07-13 06:52:48 -07:00
</div>
))}
</div>
</div>
<div className="grid grid-flow-col grid-cols-1 grid-rows-2 md:grid-cols-2 md:grid-rows-1 gap-2 sm:gap-4">
2021-07-13 06:52:48 -07:00
<div
className="border border-th-bkg-3 relative p-4 rounded-md"
style={{ height: '300px' }}
>
<Chart
2021-09-06 10:28:35 -07:00
title="Avg. Hourly Funding Rate"
2021-07-13 06:52:48 -07:00
xAxis="time"
yAxis="fundingRate"
data={perpsData}
2021-09-06 10:28:35 -07:00
labelFormat={(x) => `${x.toFixed(4)}%`}
type="area"
/>
</div>
<div
className="border border-th-bkg-3 relative p-4 rounded-md"
style={{ height: '300px' }}
>
<Chart
title="Open Interest"
xAxis="time"
yAxis="openInterest"
data={perpsData}
labelFormat={(x) =>
x &&
2021-08-30 17:24:04 -07:00
x.toLocaleString(undefined, {
maximumFractionDigits: selectedMarketConfig.baseDecimals,
}) + selectedMarketConfig.baseSymbol
}
type="area"
2021-07-13 06:52:48 -07:00
/>
</div>
</div>
</>
)
}
const AssetHeader = ({ asset }) => {
switch (asset) {
case 'BTC-PERP':
2021-07-13 06:52:48 -07:00
return (
<div className="flex items-center text-xl text-th-fgd-1">
<img
src={icons[asset]}
alt={icons[asset]}
width="24"
height="24"
className="mr-2.5"
/>
2021-08-30 17:24:04 -07:00
Bitcoin Perpetual Futures
2021-07-13 06:52:48 -07:00
</div>
)
case 'ETH-PERP':
2021-07-13 06:52:48 -07:00
return (
<div className="flex items-center text-xl text-th-fgd-1">
<img
src={icons[asset]}
alt={icons[asset]}
width="24"
height="24"
className="mr-2.5"
/>
2021-08-30 17:24:04 -07:00
Ethereum Perpetual Futures
2021-07-13 06:52:48 -07:00
</div>
)
case 'SOL-PERP':
2021-07-13 06:52:48 -07:00
return (
<div className="flex items-center text-xl text-th-fgd-1">
<img
src={icons[asset]}
alt={icons[asset]}
width="24"
height="24"
className="mr-2.5"
/>
2021-08-30 17:24:04 -07:00
Solana Perpetual Futures
2021-07-13 06:52:48 -07:00
</div>
)
case 'SRM':
return (
<div className="flex items-center text-xl text-th-fgd-1">
<img
src={icons[asset]}
alt={icons[asset]}
width="24"
height="24"
className="mr-2.5"
/>
2021-08-30 17:24:04 -07:00
Serum Perpetual Futures
2021-07-13 06:52:48 -07:00
</div>
)
default:
return (
<div className="flex items-center text-xl text-th-fgd-1">
<img
src={icons[asset]}
alt={icons[asset]}
width="24"
height="24"
className="mr-2.5"
/>
2021-08-30 17:24:04 -07:00
Bitcoin Perpetual Futures
2021-07-13 06:52:48 -07:00
</div>
)
}
}