diff --git a/README.md b/README.md index 7854d82..02871f1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Mars Protocol Rsik Dashboard +# Mars Protocol Risk Dashboard ![https://risk.marsprotocol.io](https://raw.githubusercontent.com/mars-protocol/mars-rd-fe/main/public/banner.png) diff --git a/src/api/perps/getPerpsGlobalStats.ts b/src/api/perps/getPerpsGlobalStats.ts index 533d657..794ffa1 100644 --- a/src/api/perps/getPerpsGlobalStats.ts +++ b/src/api/perps/getPerpsGlobalStats.ts @@ -1,16 +1,22 @@ import chains from 'chains' import { DEFAULT_PERPS_GLOBAL_DATA } from 'constants/chartData' +import { TIMEFRAME } from 'constants/timeframe' import { getApiBaseUrl } from 'utils/api' import { getCurrentChainId } from 'utils/getCurrentChainId' -export default async function getPerpsGlobalStats(timeframe: string = '7') { +export default async function getPerpsGlobalStats(timeframe: string = '30') { const chainId = getCurrentChainId() const chainConfig = chains[chainId] if (!chainConfig.perps) return try { const baseUrl = getApiBaseUrl() + const timeframeConfig = TIMEFRAME.find((t) => t.value === timeframe) || { + granularity: 'day', + value: Number(timeframe), + } + const url = new URL( - `${baseUrl}/v2/perps_overview?chain=neutron&days=${timeframe}&product=creditmanager&response_type=global`, + `${baseUrl}/v2/perps_overview?chain=neutron&granularity=${timeframeConfig.granularity}&unit=${timeframeConfig.value}&response_type=global`, ) const response = await fetch(url.toString()) const data = (await response.json()) as PerpsGlobalOverview diff --git a/src/api/perps/getPerpsMarketStats.ts b/src/api/perps/getPerpsMarketStats.ts index ed7f5e9..c334dc1 100644 --- a/src/api/perps/getPerpsMarketStats.ts +++ b/src/api/perps/getPerpsMarketStats.ts @@ -1,13 +1,18 @@ +import { TIMEFRAME } from 'constants/timeframe' import { getApiBaseUrl } from 'utils/api' export default async function getPerpsMarketStats( market: string = 'untrn', - timeframe: string = '7', + timeframe: string = '30', ) { try { const baseUrl = getApiBaseUrl() + const timeframeConfig = TIMEFRAME.find((t) => t.value === timeframe) || { + granularity: 'day', + value: Number(timeframe), + } const url = new URL( - `${baseUrl}/v2/perps_overview?chain=neutron&days=${timeframe}&product=creditmanager&response_type=market&market=${market}`, + `${baseUrl}/v2/perps_overview?chain=neutron&granularity=${timeframeConfig.granularity}&unit=${timeframeConfig.value}&market=${market}&response_type=market`, ) const response = await fetch(url.toString()) const data = (await response.json()) as PerpsMarketOverview diff --git a/src/components/common/Chart/SynchronizedChart/SynchronizedChartBody.tsx b/src/components/common/Chart/SynchronizedChart/SynchronizedChartBody.tsx index a486f80..f3d3534 100644 --- a/src/components/common/Chart/SynchronizedChart/SynchronizedChartBody.tsx +++ b/src/components/common/Chart/SynchronizedChart/SynchronizedChartBody.tsx @@ -99,7 +99,7 @@ export default function SynchronizedChartBody(props: Props) { } return ( -
+
+
} diff --git a/src/components/main/overview/OverviewCharts.tsx b/src/components/main/overview/OverviewCharts.tsx index 07e389e..667f3de 100644 --- a/src/components/main/overview/OverviewCharts.tsx +++ b/src/components/main/overview/OverviewCharts.tsx @@ -13,7 +13,7 @@ import { useOverviewChartData } from 'hooks/tokenomics/useOverviewChartData' import { useState } from 'react' export default function OverviewCharts() { - const [selectedTimeframe, setSelectedTimeframe] = useState(TIMEFRAME[1].value) + const [selectedTimeframe, setSelectedTimeframe] = useState(TIMEFRAME[2].value) const { data: overviewData, diff --git a/src/components/main/overview/StatsMetrics.tsx b/src/components/main/overview/StatsMetrics.tsx index 0895fac..cbdf04d 100644 --- a/src/components/main/overview/StatsMetrics.tsx +++ b/src/components/main/overview/StatsMetrics.tsx @@ -52,7 +52,7 @@ export default function StatsMetrics() { +
} diff --git a/src/components/main/perps/PerpsMetrics.tsx b/src/components/main/perps/PerpsMetrics.tsx index ff697da..12e4db0 100644 --- a/src/components/main/perps/PerpsMetrics.tsx +++ b/src/components/main/perps/PerpsMetrics.tsx @@ -1,21 +1,16 @@ import MetricsCard from 'components/common/Card/MetricsCard' +import usePerpsStats from 'hooks/perps/usePerpsGlobalStats' +import { BN } from 'utils/helpers' import { GridLandscape } from 'components/common/Icons' import { BN_ZERO } from 'constants/math' import { PRICE_ORACLE_DECIMALS } from 'constants/query' -import usePerpsStats from 'hooks/perps/usePerpsGlobalStats' -import { BN } from 'utils/helpers' export default function PerpsMetrics() { const { data: perpsStats, isLoading: perpsStatsLoading } = usePerpsStats('total', '90') - const totalTradingVolume = perpsStats?.daily_trading_volume.reduce( - (acc, day) => acc.plus(BN(day.value || 0)), - BN_ZERO, - ) - const perpsMetrics: Metric[] = [ { - value: totalTradingVolume?.shiftedBy(-PRICE_ORACLE_DECIMALS) || BN(0), + value: BN(perpsStats?.cumulative_trading_volume || 0).shiftedBy(-PRICE_ORACLE_DECIMALS), label: 'Total Trading Volume', isCurrency: true, formatOptions: { @@ -51,7 +46,9 @@ export default function PerpsMetrics() { showSignPrefix: true, }, { - value: BN(perpsStats?.fees.trading_fee[0]?.value || 0).shiftedBy(-PRICE_ORACLE_DECIMALS), + value: BN(perpsStats?.fees?.realized_trading_fee?.[0]?.value || 0).shiftedBy( + -PRICE_ORACLE_DECIMALS, + ), label: 'Total Trading Fees', isCurrency: true, formatOptions: { @@ -66,7 +63,7 @@ export default function PerpsMetrics() { +
} diff --git a/src/components/main/perps/perpsMarketStats/StatisticsPanel.tsx b/src/components/main/perps/perpsMarketStats/StatisticsPanel.tsx index 0e68e04..0d33de0 100644 --- a/src/components/main/perps/perpsMarketStats/StatisticsPanel.tsx +++ b/src/components/main/perps/perpsMarketStats/StatisticsPanel.tsx @@ -40,7 +40,7 @@ export default function StatisticsPanel(props: Props) { const metrics: Metric[] = [ { - value: getLatestValue(data.daily_trading_volume), + value: getLatestValue(data.trading_volume), label: 'Daily Trading Volume', isCurrency: true, formatOptions: { maxDecimals: 2, minDecimals: 2, abbreviated: true }, @@ -56,7 +56,7 @@ export default function StatisticsPanel(props: Props) { }, }, { - value: getLatestValue(data.fees.trading_fee || []), + value: getLatestValue(data.fees.realized_trading_fee || []), label: 'Trading Fees', isCurrency: true, formatOptions: { abbreviated: true }, diff --git a/src/components/main/perps/perpsMarketStats/index.tsx b/src/components/main/perps/perpsMarketStats/index.tsx index 5f181c6..f435839 100644 --- a/src/components/main/perps/perpsMarketStats/index.tsx +++ b/src/components/main/perps/perpsMarketStats/index.tsx @@ -35,11 +35,12 @@ export default function PerpsMarketStats(props: Props) { openInterestData, fundingRateData, pnlData, + realizedPnlBreakdown, feesData, skewData, imbalanceRatioData, notionalLiquidatedData, - dailyTradingVolumeData, + tradingVolumeData, vaultData, combinedMetricsData, } = usePerpsChartData(perpsStats || DEFAULT_PERPS_GLOBAL_DATA) @@ -93,6 +94,33 @@ export default function PerpsMarketStats(props: Props) { }) }, [pnlData]) + const netFundingFeesData = useMemo(() => { + return feesData.map((item) => { + const netFundingFees = BN(item.realized_net_funding_fees) + .plus(BN(item.unrealized_net_funding_fees)) + .toNumber() + + return { + ...item, + netTotal: netFundingFees, + } + }) + }, [feesData]) + + const realizedPnlBreakdownData = useMemo(() => { + return realizedPnlBreakdown.map((item) => { + const total = BN(item.realized_net_funding_fees) + .plus(BN(item.realized_trading_fees)) + .plus(BN(item.realized_price_pnl)) + .toNumber() + + return { + ...item, + total, + } + }) + }, [realizedPnlBreakdown]) + if (perpsStatsLoading) return (
@@ -113,10 +141,10 @@ export default function PerpsMarketStats(props: Props) {
@@ -124,7 +152,7 @@ export default function PerpsMarketStats(props: Props) { data={feesData} lines={PERPS_CHART_CONFIGS.tradingFees} height='h-80' - title='Cumulative Realized Trading and Net Funding Fees' + title='Cumulative Realized Trading and Funding Fees' />
@@ -186,7 +214,6 @@ export default function PerpsMarketStats(props: Props) { +
+
+ +
+
+ +
+
{!isGlobalStats && ( diff --git a/src/constants/chartData.ts b/src/constants/chartData.ts index 4caca6b..f0d4123 100644 --- a/src/constants/chartData.ts +++ b/src/constants/chartData.ts @@ -4,10 +4,11 @@ export const CHART_COLORS = { primary: '#30E0A1', secondary: '#AB47BC', tertiary: '#580DA3', + quaternary: '#3DB2FF', } export const DEFAULT_PERPS_GLOBAL_DATA: PerpsGlobalData = { - daily_trading_volume: [], + trading_volume: [], open_interest: { long: [], short: [], @@ -20,16 +21,17 @@ export const DEFAULT_PERPS_GLOBAL_DATA: PerpsGlobalData = { skew: [], imbalance_long_ratio: [], imbalance_short_ratio: [], - max_skew: [], }, funding_and_pnl: { funding_rate: [], unrealized_pnl: [], realized_pnl: [], + realized_price_pnl: [], }, fees: { - trading_fee: [], - net_funding_fee: [], + realized_trading_fee: [], + realized_net_funding_fee: [], + unrealized_net_funding_fee: [], }, vault_data: { deposit: [], @@ -37,6 +39,7 @@ export const DEFAULT_PERPS_GLOBAL_DATA: PerpsGlobalData = { vault_collateralization_ratio: [], }, notional_liquidated: [], + cumulative_trading_volume: '0', notional_at_risk: '0', accounts_at_risk: '0', total_accounts: '0', @@ -58,9 +61,15 @@ export const PERPS_CHART_TRANSFORMATIONS = { { path: ['funding_and_pnl', 'unrealized_pnl'], targetKey: 'unrealized' }, { path: ['funding_and_pnl', 'realized_pnl'], targetKey: 'realized' }, ], + realizedPnlBreakdown: [ + { path: ['fees', 'realized_trading_fee'], targetKey: 'realized_trading_fees' }, + { path: ['fees', 'realized_net_funding_fee'], targetKey: 'realized_net_funding_fees' }, + { path: ['funding_and_pnl', 'realized_price_pnl'], targetKey: 'realized_price_pnl' }, + ], fees: [ - { path: ['fees', 'trading_fee'], targetKey: 'trading_fees' }, - { path: ['fees', 'net_funding_fee'], targetKey: 'net_funding_fees' }, + { path: ['fees', 'realized_trading_fee'], targetKey: 'realized_trading_fees' }, + { path: ['fees', 'realized_net_funding_fee'], targetKey: 'realized_net_funding_fees' }, + { path: ['fees', 'unrealized_net_funding_fee'], targetKey: 'unrealized_net_funding_fees' }, ], skewData: [ { path: ['skew_data', 'skew'], targetKey: 'skew' }, @@ -85,7 +94,7 @@ export const PERPS_CHART_TRANSFORMATIONS = { ], singleMetrics: [ { path: ['notional_liquidated'], targetKey: 'notional_liquidated' }, - { path: ['daily_trading_volume'], targetKey: 'daily_trading_volume' }, + { path: ['trading_volume'], targetKey: 'trading_volume' }, { path: ['skew_data', 'imbalance_long_ratio'], targetKey: 'imbalance_long' }, ], combinedMetrics: [ @@ -146,8 +155,16 @@ export const PERPS_CHART_CONFIGS = { ], }, tradingFees: [ - { dataKey: 'trading_fees', color: CHART_COLORS.secondary, name: 'Trading Fees' }, - { dataKey: 'net_funding_fees', color: CHART_COLORS.tertiary, name: 'Net Funding Fees' }, + { + dataKey: 'realized_trading_fees', + color: CHART_COLORS.secondary, + name: 'Realized Trading Fees', + }, + { + dataKey: 'realized_net_funding_fees', + color: CHART_COLORS.tertiary, + name: 'Realized Funding Fees', + }, ], skew: [ { dataKey: 'skew', color: CHART_COLORS.tertiary, name: 'Skew' }, @@ -173,9 +190,9 @@ export const PERPS_CHART_CONFIGS = { ], tradingVolume: [ { - dataKey: 'daily_trading_volume', + dataKey: 'trading_volume', color: CHART_COLORS.primary, - name: 'Daily Trading Volume', + name: 'Trading Volume', }, ], imbalanceRatio: [ @@ -207,6 +224,45 @@ export const PERPS_CHART_CONFIGS = { isPercentage: true, }, ], + netFundingFees: [ + { + dataKey: 'realized_net_funding_fees', + color: CHART_COLORS.tertiary, + name: 'Realized Funding Fees', + }, + { + dataKey: 'unrealized_net_funding_fees', + color: CHART_COLORS.secondary, + name: 'Unrealized Funding Fees', + }, + { + dataKey: 'netTotal', + color: CHART_COLORS.primary, + name: 'Net Funding Fees', + }, + ], + realizedPnlBreakdown: [ + { + dataKey: 'realized_trading_fees', + color: CHART_COLORS.tertiary, + name: 'Realized Trading Fees', + }, + { + dataKey: 'realized_net_funding_fees', + color: CHART_COLORS.secondary, + name: 'Realized Funding Fees', + }, + { + dataKey: 'realized_price_pnl', + color: CHART_COLORS.quaternary, + name: 'Realized Price PnL', + }, + { + dataKey: 'total', + color: CHART_COLORS.primary, + name: 'Total', + }, + ], totalPnl: [ { dataKey: 'realized', diff --git a/src/constants/timeframe.ts b/src/constants/timeframe.ts index 05f0b31..aee556c 100644 --- a/src/constants/timeframe.ts +++ b/src/constants/timeframe.ts @@ -1,5 +1,6 @@ export const TIMEFRAME = [ - { value: '7', label: '7D' }, - { value: '30', label: '30D' }, - { value: '90', label: '90D' }, + { value: '24', label: '24H', granularity: 'hour' }, + { value: '7', label: '7D', granularity: 'day' }, + { value: '30', label: '30D', granularity: 'day' }, + { value: '90', label: '90D', granularity: 'day' }, ] diff --git a/src/hooks/perps/usePerpsChartData.ts b/src/hooks/perps/usePerpsChartData.ts index 3fbe11b..b6c30e0 100644 --- a/src/hooks/perps/usePerpsChartData.ts +++ b/src/hooks/perps/usePerpsChartData.ts @@ -7,12 +7,12 @@ export const usePerpsChartData = (data: PerpsGlobalData | PerpsMarketData) => { const validDates = useMemo(() => { const daysWithActivity = rawFeesData.filter( - (day) => day.trading_fees !== 0 || day.net_funding_fees !== 0, + (day) => day.realized_trading_fees !== 0 || day.realized_net_funding_fees !== 0, ) return new Set( daysWithActivity.length > 1 ? rawFeesData - .filter((day) => day.trading_fees !== 0 || day.net_funding_fees !== 0) + .filter((day) => day.realized_trading_fees !== 0 || day.realized_net_funding_fees !== 0) .map((day) => day.date) : rawFeesData.map((day) => day.date), ) @@ -29,6 +29,9 @@ export const usePerpsChartData = (data: PerpsGlobalData | PerpsMarketData) => { useChartDataTransform(data, PERPS_CHART_TRANSFORMATIONS.fundingRate), ) const pnlData = filterData(useChartDataTransform(data, PERPS_CHART_TRANSFORMATIONS.pnl)) + const realizedPnlBreakdown = filterData( + useChartDataTransform(data, PERPS_CHART_TRANSFORMATIONS.realizedPnlBreakdown), + ) const feesData = filterData(useChartDataTransform(data, PERPS_CHART_TRANSFORMATIONS.fees)) const skewData = filterData(useChartDataTransform(data, PERPS_CHART_TRANSFORMATIONS.skewData)) const vaultData = filterData(useChartDataTransform(data, PERPS_CHART_TRANSFORMATIONS.vaultData)) @@ -43,10 +46,11 @@ export const usePerpsChartData = (data: PerpsGlobalData | PerpsMarketData) => { openInterestData, fundingRateData, pnlData, + realizedPnlBreakdown, feesData, skewData, notionalLiquidatedData: singleMetrics, - dailyTradingVolumeData: singleMetrics, + tradingVolumeData: singleMetrics, imbalanceRatioData: singleMetrics, vaultData, combinedMetricsData, diff --git a/src/pages/PerpsOverviewPage.tsx b/src/pages/PerpsOverviewPage.tsx index f19e36e..d6b6275 100644 --- a/src/pages/PerpsOverviewPage.tsx +++ b/src/pages/PerpsOverviewPage.tsx @@ -19,7 +19,7 @@ export default function PerpsOverviewPage() { const [searchParams] = useSearchParams() const isIframeView = searchParams.get('iframeView') === 'on' - const [selectedTimeframe, setSelectedTimeframe] = useState(TIMEFRAME[1].value) + const [selectedTimeframe, setSelectedTimeframe] = useState(TIMEFRAME[2].value) const [selectedOption, setSelectedOption] = useState(asset ? `perps/${asset}` : 'total') const handleSelectChange = (value: string) => { diff --git a/src/types/app.d.ts b/src/types/app.d.ts index 04dda3a..b1ea2d7 100644 --- a/src/types/app.d.ts +++ b/src/types/app.d.ts @@ -1495,6 +1495,7 @@ interface Token { interface TimeframeOption { value: string label: string + granularity?: string } interface MergedChartData { @@ -1548,18 +1549,19 @@ interface PerpsSkewData { skew: DateValue[] imbalance_long_ratio: DateValue[] imbalance_short_ratio: DateValue[] - max_skew: DateValue[] } interface PerpsFundingAndPnL { funding_rate: DateValue[] unrealized_pnl: DateValue[] realized_pnl: DateValue[] + realized_price_pnl: DateValue[] } interface PerpsFees { - trading_fee: DateValue[] - net_funding_fee: DateValue[] + realized_trading_fee: DateValue[] + realized_net_funding_fee: DateValue[] + unrealized_net_funding_fee: DateValue[] } interface PerpsVaultData { @@ -1569,13 +1571,14 @@ interface PerpsVaultData { } interface PerpsGlobalData { - daily_trading_volume: DateValue[] + trading_volume: DateValue[] open_interest: PerpsOpenInterest skew_data: PerpsSkewData funding_and_pnl: PerpsFundingAndPnL fees: PerpsFees vault_data: PerpsVaultData notional_liquidated: DateValue[] + cumulative_trading_volume: string notional_at_risk: string accounts_at_risk: string total_accounts: string @@ -1586,11 +1589,12 @@ interface PerpsGlobalOverview { } interface PerpsMarketData { denom: string - daily_trading_volume: DateValue[] + trading_volume: DateValue[] open_interest: PerpsOpenInterest skew_data: PerpsSkewData funding_and_pnl: PerpsFundingAndPnL fees: PerpsFees + cumulative_trading_volume: string notional_at_risk: string accounts_at_risk: string total_accounts: string diff --git a/tailwind.config.js b/tailwind.config.js index 06f0745..37ddfa5 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -192,6 +192,7 @@ module.exports = { 65: '250px', 75: '300px', 100: '400px', + 140: '560px', 'screen-full': '100dvh', 'screen/90': '90dvh', 'screen/80': '80dvh',