tradingview-apilightweight-charts

Chart showing the intraday datetime incorrectly?


With this sample of data from my backend server (note the datetimes and timezone):

{2024-01-09 19:25:00 -0500 EST 473.94 474 473.94 474 10}
{2024-01-09 19:30:00 -0500 EST 473.95 473.96 473.93 473.96 6}
{2024-01-09 19:35:00 -0500 EST 473.99 473.99 473.99 473.99 5}
{2024-01-09 19:40:00 -0500 EST 474 474.01 473.94 473.95 16}
{2024-01-09 19:45:00 -0500 EST 473.89 473.89 473.81 473.81 150}
{2024-01-09 19:50:00 -0500 EST 473.83 473.83 473.83 473.83 1}
{2024-01-09 19:55:00 -0500 EST 473.77 473.83 473.77 473.79 142}

...why on the chart do the datetimes show up completely incorrectly?

CurrentOutput

This is the tail end of the chart, which does not show the datetime as it's being received from the backend. (Not sure if it's relevant but my local offset is -0600, which is the 6 hours difference between the data and what's showing on the chart?)

I've researched that this could be due to a timezone issue, but I'm confused (and even the article only suggests "hacky" ways) on how to get this implemented properly. Shouldn't the chart just display the datetimes as they're being given to the chart? Or does the chart convert the datetimes to +0000 UTC first before displaying? The code for my chart on the webpage looks like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>HELLO</title>
    <!-- Include the lightweight-charts library directly from CDN -->
    <script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
    <script type="text/javascript">
        document.addEventListener('DOMContentLoaded', function () {
            // Specify the ID of the div where you want to load the chart
            var chartDivId = 'chart-container';

            // Create the chart inside the specified div
            var chart = LightweightCharts.createChart(chartDivId, {
                width: 600,
                height: 300,
                layout: {
                    background: {
                        type: 'solid',
                        color: '#000000',
                    },
                    textColor: 'rgba(255, 255, 255, 0.9)',
                },
                grid: {
                    vertLines: {
                        color: 'rgba(197, 203, 206, 0.5)',
                    },
                    horzLines: {
                        color: 'rgba(197, 203, 206, 0.5)',
                    },
                },
                crosshair: {
                    mode: LightweightCharts.CrosshairMode.Normal,
                },
                rightPriceScale: {
                    borderColor: 'rgba(197, 203, 206, 0.8)',
                },
                timeScale: {
                    borderColor: 'rgba(197, 203, 206, 0.8)',
                    timeVisible: true,
                    secondsVisible: true,
                },
            });

            // Create the candlestick series
            var candleSeries = chart.addCandlestickSeries({
                upColor: 'rgba(0, 255, 0, 1)',
                downColor: 'rgba(255, 0, 0, 1)',
                borderDownColor: 'rgba(255, 0, 0, 1)',
                borderUpColor: 'rgba(0, 255, 0, 1)',
                wickDownColor: 'rgba(255, 0, 0, 1)',
                wickUpColor: 'rgba(0, 255, 0, 1)',
            });

            // Function to load chart data from the backend
            async function loadChartData() {

                // Query the localhost:8081/getLatestData endpoint, which returns json.NewEncoder(w).Encode([]types.TOHLCV),
                // wait for the response, then parse the response as json, and then set the newData variable to the response.
                await fetch('http://localhost:8081/getLatestData')
                    .then(response => response.json())
                    .then(data => dataFromBackend = data);

                // Format the (datetime) data to the format that the charting library expects
                // https://stackoverflow.com/a/76397648/10018602
                const formattedData = dataFromBackend.map(data => ({
                    time: (new Date(data.time)).getTime() / 1000,
                    open: data.open,
                    high: data.high,
                    low: data.low,
                    close: data.close,
                    volume: data.volume,
                }));

                // Set new data to the candlestick series
                candleSeries.setData(formattedData);
            }

            // Initial load of data
            loadChartData();

            // Set an interval to reload chart data every 5 seconds
            setInterval(loadChartData, 5000);
        });
    </script>
</head>
<body>
    <h1>Welcome</h1>

    <!-- Specify the ID of the div where you want to load the chart -->
    <div id="chart-container"></div>
</body>
</html>

I've tried implementing this solution, but then it just breaks the datetimes altogether, showing the year as '94 or something weird, so that didn't help. What should I try next?

...
               // https://tradingview.github.io/lightweight-charts/docs/time-zones
                // ...because tradingview didn't consider timezones....
                function timeToTz(originalTime, timeZone) {
                    const zonedDate = new Date(new Date(originalTime * 1000).toLocaleString('en-US', { timeZone }));
                    return zonedDate.getTime() / 1000;
                }

                // Format the (datetime) data to the format that the charting library expects
                // https://stackoverflow.com/a/76397648/10018602
                const formattedData = dataFromBackend.map(data => ({
                    time: timeToTz((new Date(data.time)), "America/New_York"), //(new Date(data.time)).getTime() / 1000,
                    open: data.open,
                    high: data.high,
                    low: data.low,
                    close: data.close,
                    volume: data.volume,
                }));

                // Set new data to the candlestick series
                candleSeries.setData(formattedData);
...

Solution

  • I am having the same issue. It appears that lightweight-charts is attempting to do some sort of timezone conversion on the datetime.

    I dumped my dataframe to a csv and then removed the "-05:00" part of the datetime column and then reimported it to a df. Using this modified df the chart displays the date correctly in my local timezone.

    I'm also trying to figure out why it does this. I will update what I find.

    Update: I discovered that the issue is that ib_insync module returns date in this format "2024-01-11 15:55:00-06:00". Whenever lightweight-charts does the date format conversion, the "-6:00" causes it to incorrectly convert. To get around this, I formatted the date column before charting it and this displayed the candles with the correct date:

    df['date'] = df['date'].dt.strftime("%Y-%m-%d %H:%M:%S")