tradingview / lightweight-charts

Performant financial charts built with HTML5 canvas
https://www.tradingview.com/lightweight-charts/
Apache License 2.0
9.63k stars 1.65k forks source link

Assertion failed: data must be asc ordered by time. TIME DIPLICATES #1700

Open Yaroslav-Bulavin opened 2 months ago

Yaroslav-Bulavin commented 2 months ago

Lightweight Charts™ Version: ^4.2.0

Steps/code to reproduce:

From the error I understand that I have the same time property for a few entries: Assertion failed: data must be asc ordered by time, index=3, time=1727077163.929, prev time=1727077163.929;

I using data from the backend to get coin price history, approximately 400 items in the array. Array item:

currency_code: "BTC"
high24hr: "64146.2"
id: 1161674
low24hr: "62603.6"
price: "62890.13"
timestamp: "2024-09-20T13:57:28.947Z"
vol24hr: "238901766.70551"

I have an endpoint that saves data in storage:

getCoinPriceHistory: builder.query<GetCoinPriceHistory, { coin: CoinEnum }>(
      {
        query: ({ coin }) => ({
          url: `/prices/${coin}`,
        }),
        async onQueryStarted(id, { dispatch, queryFulfilled }) {
          try {
            const { data } = await queryFulfilled;
            console.log('DUPLICATES', findDuplicateDatesWithTime(data?.prices));
            dispatch(SET_COIN_PRICES(data?.prices || []));
          } catch (err) {}
        },
        providesTags: [ApiTagsEnum.GET_COIN_PRICE_HISTORY],
      },
    ),

I added console.log to find duplications in the response data. The findDuplicateDatesWithTime function:

function findDuplicateDatesWithTime(data: CurrencyType[]) {
  const duplicates = data.filter((x, i, arr) => {
    return arr.some(
      (y) =>
        new Date(y.timestamp).getTime() / 1000 === new Date(x.timestamp).getTime() / 1000 &&
        y.id !== x.id,
    );
  });
  return duplicates;
}

But it logs me empty array every time(No duplicates have been found). So BE response is valid.

Screenshot 2024-09-23 at 10 59 39

This is how I use timestamp from the response:

const formattedInitPrices = coinPrices.map((info) => ({
      time: (new Date(info.timestamp).getTime() / 1000) as Time,
      value: Number(info?.price),
    }));

The strangest thing is that sometimes it works without any errors after the page refresh, but sometimes it crashes.

Tech stack: react, vite, lightweight-charts, typescript, redux-toolkit

Actual behavior:

Error Assertion failed: data must be asc ordered by time, index=3, time=1727077163.929, prev time=1727077163.929;

Expected behavior:

It should work whenever BE response has unique entries in the array

Screenshots:

CodeSandbox/JSFiddle/etc link:

SlicedSilver commented 2 months ago

I suspect that your findDuplicateDatesWithTime isn't correctly identifying duplicate timestamps. Any two items which have the same time value would be considered a duplicate even if everything else was different. In your case, you code is checking if the timestamp values match but also check that the id values match. So perhaps try removing && y.id !== x.id and you'll find the duplicate values.

ambrizals commented 2 months ago

Yeah same i was also getting this error, but actually i have different dateTime but i don't know why chart was flagging this as duplicate. image (2)

SlicedSilver commented 2 months ago

Are the actual time values that you provide to setData different?

At some point you will be converting your "date" value into a time value.

ambrizals commented 2 months ago

Are the actual time values that you provide to setData different?

At some point you will be converting your "date" value into a time value.

Yeah of course i do some conversion into mili epoch using new Date().getTime(), even i was trying to divide it 1000 i still can't setData

const milis = dateObj.getTime() / 1000

Screenshot 2024-10-01 at 11 07 32

SlicedSilver commented 2 months ago

The error is suggesting that there are some data points which are either duplicates or multiple data points for the same timestamp.

This can sometimes occur when using external data sources that they will provide more than one data point for timestamp.

For example:

[
  { date: '2014-02-01T12:34:56.789', value: 123 },
  { date: '2014-02-01T12:34:56.789', value: 125 }, // same timestamp as previous
  { date: '2014-02-01T12:35:00.000', value: 125 },
]

You could log your data before providing it to setData to double check.

Yaroslav-Bulavin commented 2 months ago

I suspect that your findDuplicateDatesWithTime isn't correctly identifying duplicate timestamps. Any two items which have the same time value would be considered a duplicate even if everything else was different. In your case, you code is checking if the timestamp values match but also check that the id values match. So perhaps try removing && y.id !== x.id and you'll find the duplicate values.

@SlicedSilver it doesn't make sense. If I remove y.id !== x.id comparison, the script will return me all the initial array, because using y.id !== x.I find If my array has entries with the same time but different id. Id field is always unique.

SlicedSilver commented 2 months ago

So you want to keep data points with the same timestamp as long as the ids are different?

Therefore you are okay with this:

[
  { currency_code: "BTC", id: 1161674, price: "62890.00", timestamp:, "2024-09-20T13:57:28.947Z" },
  { currency_code: "BTC", id: 1161675, price: "62892.00", timestamp:, "2024-09-20T13:57:28.947Z" }, // same timestamp as previous
]

Well the library will see this as a problem because you have two data points at the same timestamp. Which one should it plot? because it can only plot one.

Id field is always unique.

Therefore the findDuplicateDatesWithTime will never find any timestamp duplicates because the ids will never match.

safaritrader commented 1 month ago

if you want too keep duplicates an easy way is to pull that duplicates out of that array and add it to another series! or another way is to use index instead of time and assign the real time to another key for identification.