Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 110 additions & 16 deletions src/features/dex/components/charts/PairLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,94 @@ export function PairLineChart({

// Watch for data changes
useEffect(() => {
if (!data?.result?.length || !areaSeries.current) {
if (!areaSeries.current) {
return;
}

// Clear existing data first
areaSeries.current.setData([]);

// If no data, leave chart empty
if (!data?.result?.length) {
return;
}

// Update with new data
updateSeriesData(data as ChartResponse);
}, [data]);
const chartData = data as ChartResponse;
const now = moment().unix();

// Calculate the minimum timestamp based on selected timeframe
let minTime: number;
switch (performanceChartTimeframe) {
case '1d':
minTime = now - (24 * 3600); // 24 hours ago
break;
case '7d':
minTime = now - (7 * 24 * 3600); // 7 days ago
break;
case '30d':
minTime = now - (30 * 24 * 3600); // 30 days ago
break;
default:
minTime = 0; // Show all data
}

function updateSeriesData(chartData: ChartResponse) {
// Chart library constraints
const MAX_CHART_VALUE = 90071992547409.91;
const MIN_CHART_VALUE = -90071992547409.91;

// Map all data with timestamps
const allDataPoints = chartData.result
.map((item) => {
const itemTime = moment(item.end_time).unix();
return {
time: itemTime as UTCTimestamp,
value: Number(item.last_price),
originalTime: itemTime,
};
})
.sort((a, b) => a.originalTime - b.originalTime);

// Filter data to only include points within the selected timeframe
const filteredData = allDataPoints
.filter((item) => item.originalTime >= minTime)
.map(({ time, value }) => ({ time, value }));

// If no data in timeframe, show a flat line at the last known price
if (filteredData.length === 0) {
// Find the most recent data point before the timeframe
const lastKnownPoint = allDataPoints
.filter((item) => item.originalTime < minTime)
.pop(); // Get the last item (most recent before timeframe)

if (lastKnownPoint) {
// Apply scaling to the last known value if needed
const rawValue = lastKnownPoint.value;
const maxPrice = rawValue;
let scaleFactor = 1;
if (maxPrice > MAX_CHART_VALUE) {
scaleFactor = Math.pow(10, Math.ceil(Math.log10(maxPrice / MAX_CHART_VALUE)));
}
const scaledValue = rawValue / scaleFactor;
const clampedValue = Math.max(MIN_CHART_VALUE, Math.min(MAX_CHART_VALUE, scaledValue));

// Create a flat line spanning the entire timeframe
const flatLineData = [
{ time: minTime as UTCTimestamp, value: clampedValue },
{ time: now as UTCTimestamp, value: clampedValue },
];
areaSeries.current.setData(flatLineData);
chart?.timeScale().fitContent();
return;
} else {
// No data at all, clear chart
areaSeries.current.setData([]);
return;
}
}

// Find max value to determine if scaling is needed
const maxPrice = Math.max(...chartData.result.map(item => Number(item.last_price)));
const maxPrice = Math.max(...filteredData.map(item => item.value));

// Calculate scale factor if values are too large
let scaleFactor = 1;
Expand All @@ -129,35 +201,57 @@ export function PairLineChart({
scaleFactor = Math.pow(10, Math.ceil(Math.log10(maxPrice / MAX_CHART_VALUE)));
}

const formattedData = chartData.result
let formattedData = filteredData
.map((item) => {
const rawValue = Number(item.last_price);
const rawValue = item.value;
const scaledValue = rawValue / scaleFactor;

// Ensure value is within bounds
const clampedValue = Math.max(MIN_CHART_VALUE, Math.min(MAX_CHART_VALUE, scaledValue));

return {
time: moment(item.end_time).unix() as UTCTimestamp,
time: item.time,
value: clampedValue,
};
})
.sort((a, b) => a.time - b.time);

// if formattedData less than 10 generate more data with same value but with time - 1 hour
if (formattedData.length < 10) {
for (let i = 0; i < 10 - formattedData.length; i++) {
const lastItem = formattedData[0];
// Check if all values are the same (flat line scenario)
const allValuesSame = formattedData.every(item => item.value === formattedData[0].value);
const firstPoint = formattedData[0];
const lastPoint = formattedData[formattedData.length - 1];

// If we have only one point or all values are the same, ensure the line spans the full timeframe
if (formattedData.length === 1 || allValuesSame) {
// Ensure we have points at the start and end of the timeframe
if (firstPoint.time > minTime) {
formattedData.unshift({
time: (lastItem.time - 3600) as UTCTimestamp,
value: lastItem.value,
time: minTime as UTCTimestamp,
value: firstPoint.value,
});
}
if (lastPoint.time < now) {
formattedData.push({
time: now as UTCTimestamp,
value: lastPoint.value,
});
}
} else {
// For multiple different values, ensure minimum data points for smooth rendering
if (formattedData.length < 10 && formattedData.length > 0) {
const lastItem = formattedData[formattedData.length - 1];
for (let i = 0; i < 10 - formattedData.length; i++) {
formattedData.push({
time: (lastItem.time + (i + 1) * 3600) as UTCTimestamp,
value: lastItem.value,
});
}
}
}

areaSeries.current?.setData(formattedData);
areaSeries.current.setData(formattedData);
chart?.timeScale().fitContent();
}
}, [data, performanceChartTimeframe, chart]);

if (loading) {
return (
Expand Down
31 changes: 29 additions & 2 deletions src/features/dex/components/charts/TokenPricePerformance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,17 +242,22 @@ export default function TokenPricePerformance({
const minTime = timeFrameHours === Infinity ? 0 : now - (timeFrameHours * 3600);

// Convert data format - TradingView expects Unix timestamps in seconds
const rawData = chartData.labels
const allDataPoints = chartData.labels
.map((timestamp, index) => {
// Ensure timestamp is in seconds (not milliseconds)
const timeInSeconds = timestamp > 1e10 ? Math.floor(timestamp / 1000) : timestamp;
return {
time: timeInSeconds as any,
value: Number(chartData.data[index]) || 0,
originalTime: timeInSeconds,
};
})
.sort((a, b) => a.originalTime - b.originalTime);

// Filter data to only include points within the selected timeframe
const rawData = allDataPoints
.filter((item) => timeFrameHours === Infinity || item.time >= minTime)
.sort((a, b) => a.time - b.time); // Ensure data is sorted by time
.map(({ time, value }) => ({ time, value }));

// Remove duplicate timestamps and ensure strict ascending order
const formattedData: typeof rawData = [];
Expand Down Expand Up @@ -282,6 +287,28 @@ export default function TokenPricePerformance({
if (chartRef.current) {
chartRef.current.timeScale().fitContent();
}
} else {
// If no data in timeframe, show a flat line at the last known price
const lastKnownPoint = allDataPoints
.filter((item) => item.originalTime < minTime)
.pop(); // Get the last item (most recent before timeframe)

if (lastKnownPoint) {
// Create a flat line spanning the entire timeframe
const flatLineData = [
{ time: minTime as any, value: lastKnownPoint.value },
{ time: now as any, value: lastKnownPoint.value },
];
seriesRef.current.setData(flatLineData);

// Fit content
if (chartRef.current) {
chartRef.current.timeScale().fitContent();
}
} else {
// No data at all, clear chart
seriesRef.current.setData([]);
}
}
}, [chartData, selectedTimeFrame, isBarChart]);

Expand Down
110 changes: 94 additions & 16 deletions src/features/trending/components/TokenLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,39 +100,117 @@ export function TokenLineChart({

// Watch for data changes
useEffect(() => {
if (!data?.result?.length || !areaSeries.current) {
if (!areaSeries.current) {
return;
}

// Clear existing data first
areaSeries.current.setData([]);

// If no data, leave chart empty
if (!data?.result?.length) {
return;
}

// Update with new data
updateSeriesData(data as ChartResponse);
}, [data]);
const chartData = data as ChartResponse;
const now = moment().unix();

// Calculate the minimum timestamp based on selected timeframe
let minTime: number;
switch (performanceChartTimeframe) {
case '1d':
minTime = now - (24 * 3600); // 24 hours ago
break;
case '7d':
minTime = now - (7 * 24 * 3600); // 7 days ago
break;
case '30d':
minTime = now - (30 * 24 * 3600); // 30 days ago
break;
default:
minTime = 0; // Show all data
}

function updateSeriesData(chartData: ChartResponse) {
const formattedData = chartData.result
// Map all data with timestamps
const allDataPoints = chartData.result
.map((item) => {
const itemTime = moment(item.end_time).unix();
return {
time: moment(item.end_time).unix() as UTCTimestamp,
time: itemTime as UTCTimestamp,
value: Number(item.last_price),
originalTime: itemTime,
};
})
.sort((a, b) => a.time - b.time);
.sort((a, b) => a.originalTime - b.originalTime);

// Filter data to only include points within the selected timeframe
const filteredData = allDataPoints
.filter((item) => item.originalTime >= minTime)
.map(({ time, value }) => ({ time, value }));

// If no data in timeframe, show a flat line at the last known price
if (filteredData.length === 0) {
// Find the most recent data point before the timeframe
const lastKnownPoint = allDataPoints
.filter((item) => item.originalTime < minTime)
.pop(); // Get the last item (most recent before timeframe)

if (lastKnownPoint) {
// Create a flat line spanning the entire timeframe
const flatLineData = [
{ time: minTime as UTCTimestamp, value: lastKnownPoint.value },
{ time: now as UTCTimestamp, value: lastKnownPoint.value },
];
areaSeries.current.setData(flatLineData);
chart?.timeScale().fitContent();
return;
} else {
// No data at all, clear chart
areaSeries.current.setData([]);
return;
}
}

// If we have data, ensure it spans the full timeframe
// Check if all values are the same (flat line scenario)
const allValuesSame = filteredData.every(item => item.value === filteredData[0].value);
const firstPoint = filteredData[0];
const lastPoint = filteredData[filteredData.length - 1];

// if formattedData less than 10 generate more data with same value but with time - 1 hour
if (formattedData.length < 10) {
for (let i = 0; i < 10 - formattedData.length; i++) {
const lastItem = formattedData[0];
let formattedData = [...filteredData];

// If we have only one point or all values are the same, ensure the line spans the full timeframe
if (filteredData.length === 1 || allValuesSame) {
// Ensure we have points at the start and end of the timeframe
if (firstPoint.time > minTime) {
formattedData.unshift({
time: (lastItem.time - 3600) as UTCTimestamp,
value: lastItem.value,
time: minTime as UTCTimestamp,
value: firstPoint.value,
});
}
if (lastPoint.time < now) {
formattedData.push({
time: now as UTCTimestamp,
value: lastPoint.value,
});
}
} else {
// For multiple different values, ensure minimum data points for smooth rendering
if (formattedData.length < 10 && formattedData.length > 0) {
const lastItem = formattedData[formattedData.length - 1];
for (let i = 0; i < 10 - formattedData.length; i++) {
formattedData.push({
time: (lastItem.time + (i + 1) * 3600) as UTCTimestamp,
value: lastItem.value,
});
}
}
}

areaSeries.current?.setData(formattedData);
areaSeries.current.setData(formattedData);
chart?.timeScale().fitContent();
}
}, [data, performanceChartTimeframe, chart]);

if (loading) {
return (
Expand All @@ -156,4 +234,4 @@ export function TokenLineChart({
}


export default TokenLineChart;
export default TokenLineChart;