Skip to content

Commit 914f345

Browse files
authored
chore: spot tradingview chart QOL (dydxprotocol#2024)
1 parent 1ed6487 commit 914f345

File tree

6 files changed

+59
-46
lines changed

6 files changed

+59
-46
lines changed

src/clients/spotApi.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ export type SpotApiGetBarsQuery = {
206206
countback?: number;
207207
};
208208

209+
export enum SpotApiHistoricalBarsStatus {
210+
OK = 'ok',
211+
NO_DATA = 'no_data',
212+
}
213+
209214
export interface SpotApiGetBarsResponseData {
210215
buyVolume: string[];
211216
buyers: number[];
@@ -223,7 +228,7 @@ export interface SpotApiGetBarsResponseData {
223228
transactions: number[];
224229
volume: string[];
225230
volumeNativeToken: string[];
226-
s: string;
231+
s: SpotApiHistoricalBarsStatus;
227232
pair: Record<string, any>;
228233
}
229234

@@ -380,8 +385,7 @@ export const transformBarsResponseToBars = (
380385

381386
export const getSpotBars = async (apiUrl: string, query: SpotApiGetBarsQuery) => {
382387
const client = getOrCreateSpotApiClient(apiUrl);
383-
const response = await client.getBars(query);
384-
return transformBarsResponseToBars(response);
388+
return client.getBars(query);
385389
};
386390

387391
export const getSpotPortfolioTrades = async (apiUrl: string, address: string) => {

src/constants/candles.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,22 +122,6 @@ export const RESOLUTION_CHART_CONFIGS = {
122122
'1D': { defaultRange: 2 * timeUnits.month },
123123
} as Record<ResolutionString, { defaultRange: number }>;
124124

125-
export const SPOT_RESOLUTION_CHART_CONFIGS = {
126-
'1S': { defaultRange: 5 * timeUnits.minute },
127-
'5S': { defaultRange: 10 * timeUnits.minute },
128-
'15S': { defaultRange: 30 * timeUnits.minute },
129-
'30S': { defaultRange: timeUnits.hour },
130-
'1': { defaultRange: 2 * timeUnits.hour },
131-
'5': { defaultRange: 10 * timeUnits.hour },
132-
'15': { defaultRange: 30 * timeUnits.hour },
133-
'30': { defaultRange: timeUnits.day },
134-
'60': { defaultRange: 6 * timeUnits.day },
135-
'240': { defaultRange: 24 * timeUnits.day },
136-
'720': { defaultRange: 48 * timeUnits.day },
137-
'1D': { defaultRange: 4 * timeUnits.month },
138-
'1W': { defaultRange: timeUnits.year },
139-
} as Record<ResolutionString, { defaultRange: number }>;
140-
141125
export const RESOLUTION_STRING_TO_LABEL = {
142126
'1': { value: '1', unitStringKey: STRING_KEYS.MINUTES_ABBREVIATED },
143127
'5': { value: '5', unitStringKey: STRING_KEYS.MINUTES_ABBREVIATED },

src/constants/spot.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export const SOLANA_BASE_TRANSACTION_FEE = 0.000005;
77
export const SOL_WITHDRAWAL_POLL_INTERVAL_MS = 1000;
88
export const SOL_WITHDRAWAL_TIMEOUT_MS = 30000;
99
export const SPOT_DEFAULT_TOKEN_MINT = 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263';
10+
export const SPOT_TV_DEFAULT_VISIBLE_BAR_COUNT = 250;

src/hooks/tradingView/useSpotChartMarketAndResolution.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { useEffect, useRef } from 'react';
22

33
import type { ResolutionString } from 'public/tradingview/charting_library';
44

5-
import { DEFAULT_RESOLUTION, SPOT_RESOLUTION_CHART_CONFIGS } from '@/constants/candles';
5+
import { DEFAULT_RESOLUTION } from '@/constants/candles';
6+
import { SPOT_TV_DEFAULT_VISIBLE_BAR_COUNT } from '@/constants/spot';
67
import type { TvWidget } from '@/constants/tvchart';
78

89
import { useAppSelector } from '@/state/appTypes';
@@ -42,29 +43,43 @@ export const useSpotChartMarketAndResolution = ({
4243
tvWidget
4344
.activeChart()
4445
.onIntervalChanged()
45-
.subscribe(null, (newResolution) => {
46-
setVisibleRangeForResolution(tvWidget, newResolution);
46+
.subscribe(null, () => {
47+
setVisibleRange(tvWidget);
4748
});
4849

49-
// Set visible range on initial render
50-
setVisibleRangeForResolution(tvWidget, resolution);
50+
setVisibleRange(tvWidget);
5151
});
5252
}, [tokenMint, tvWidget]);
5353
};
5454

55-
const setVisibleRangeForResolution = (tvWidget: TvWidget, resolution: ResolutionString) => {
56-
const defaultRange: number | undefined = SPOT_RESOLUTION_CHART_CONFIGS[resolution]?.defaultRange;
57-
58-
if (defaultRange) {
59-
const to = Date.now() / 1000;
60-
const from = (Date.now() - defaultRange) / 1000;
61-
62-
tvWidget.activeChart().setVisibleRange(
63-
{
64-
from,
65-
to,
66-
},
67-
{ percentRightMargin: 10 }
68-
);
69-
}
55+
const setVisibleRange = (tvWidget: TvWidget) => {
56+
const defaultBarCount = SPOT_TV_DEFAULT_VISIBLE_BAR_COUNT;
57+
const chart = tvWidget.activeChart();
58+
59+
const handleDataLoaded = () => {
60+
chart
61+
.exportData({ includeTime: true, includeSeries: true, includedStudies: [] })
62+
.then((exportedData) => {
63+
const bars = exportedData.data;
64+
if (bars.length === 0) return;
65+
66+
const startIndex = Math.max(0, bars.length - defaultBarCount);
67+
const fromBar = bars[startIndex];
68+
const toBar = bars[bars.length - 1];
69+
const from = fromBar?.[0];
70+
const to = toBar?.[0];
71+
72+
if (from && to) {
73+
chart.setVisibleRange({ from, to }, { percentRightMargin: 10 });
74+
}
75+
})
76+
.catch(() => {
77+
// Fallback: do nothing, let TradingView use default zoom
78+
});
79+
80+
// Unsubscribe after handling
81+
chart.onDataLoaded().unsubscribe(null, handleDataLoaded);
82+
};
83+
84+
chart.onDataLoaded().subscribe(null, handleDataLoaded);
7085
};

src/lib/tradingView/spotDatafeed/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { type RootStore } from '@/state/_store';
77
import {
88
getSpotBars,
99
SpotApiGetBarsQuery,
10+
SpotApiHistoricalBarsStatus,
1011
SpotApiTokenPairStatisticsType,
12+
transformBarsResponseToBars,
1113
} from '@/clients/spotApi';
1214
import { waitForSelector } from '@/lib/asyncUtils';
1315
import {
@@ -54,7 +56,10 @@ export const getSpotDatafeed = (store: RootStore, spotApiUrl: string): IBasicDat
5456
resolveSymbol: async (tokenMint, onSymbolResolvedCallback) => {
5557
setTimeout(async () => {
5658
const tokenPrice = await waitForSelector(store, (s) => BonsaiCore.spot.tokenPrice.data(s));
57-
const symbolInfo = createSpotSymbolInfo(tokenMint, tokenPrice);
59+
const { symbol, tokenNameFull } = await waitForSelector(store, (s) =>
60+
BonsaiCore.spot.tokenMetadata.data(s)
61+
);
62+
const symbolInfo = createSpotSymbolInfo(tokenMint, tokenPrice, tokenNameFull, symbol);
5863
onSymbolResolvedCallback(symbolInfo);
5964
}, 0);
6065
},
@@ -82,12 +87,14 @@ export const getSpotDatafeed = (store: RootStore, spotApiUrl: string): IBasicDat
8287
statsType: SpotApiTokenPairStatisticsType.Filtered,
8388
};
8489

85-
const bars = await wrapAndLogBonsaiError(
90+
const barsResponse = await wrapAndLogBonsaiError(
8691
() => getSpotBars(spotApiUrl, query),
87-
'getSpotBars'
92+
'spot/getSpotBars'
8893
)();
8994

90-
if (bars.length === 0) {
95+
const bars = transformBarsResponseToBars(barsResponse);
96+
97+
if (barsResponse.data.s === SpotApiHistoricalBarsStatus.NO_DATA) {
9198
onHistoryCallback([], { noData: true });
9299
} else {
93100
const chartBars = transformSpotCandlesForChart(bars);

src/lib/tradingView/spotDatafeed/utils.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,17 @@ const getSpotPriceDecimals = (price?: number | null): number => {
5050
// Create symbol info for spot tokens
5151
export const createSpotSymbolInfo = (
5252
tokenMint: string,
53-
tokenPrice?: number | null
53+
tokenPrice?: number | null,
54+
tokenName?: string,
55+
tokenSymbol?: string
5456
): LibrarySymbolInfo => {
5557
const decimals = getSpotPriceDecimals(tokenPrice);
5658
const pricescale = 10 ** decimals;
5759

5860
return {
5961
ticker: tokenMint,
60-
name: tokenMint,
61-
description: tokenMint,
62+
name: `${tokenSymbol ?? tokenMint}/USD`,
63+
description: tokenName ?? tokenSymbol ?? tokenMint,
6264
type: 'crypto',
6365
session: '24x7',
6466
timezone,

0 commit comments

Comments
 (0)