With the current stock market volatility, I wanted to create a personal webpage using AI-powered code generation tools to plot the stock prices and have alerts to monitor the minimum and maximum values.
To build this webpage, I wanted to use only available free tools for AI, charts and retrieve the stock prices.
Reminder: The implementations described here should be used only on a local computer only, since it exposes an API-KEY which should never be available online. For a commercial webpage, it should always use a server side to request the stocks names and prices instead of making api calls on the client side.

This article describes my journey to attempt to achieve this goal using Google Gemini 1.5 and ChatGPT 4o chatbox.
Update: Added the results of the unofficial version of ChatGPT 5.
It’s also possible to call ChatGPT via API, which is preferable to those who have paid plans, but I wanted to make it easier to be reproducible by the reader by  simply using the chatbox.

At the end of the article, there is the final conclusion of this experiment and a comparison between the results generated by both  AI code assistants chatboxes.

Setup

To retrieve the stock market prices, I use twelvedata.com website, which provides an API on its free plan.
You will need to:

  • Setup an api key.

Google Gemini

I started with Google Gemini 1.5 chatbox to see how it was able to understand my request.
The prompt:

create `html` and `javascript` website:
- use bootstrap 5 and jquery.
- it should have the following input files at the left top using css classes: `form-control d-inline ms-2` and `max-width: 200px`.
  - The first input field is `ticker` and it represents a stock ticker.
  - The second field should be `refresh-rate` and it represents the time to retrieve the stocks. it should be fill with number 5.
  - The third field is `min-value` and it represents an alarm if the latest stock price is under this value.
  - The fourth field is `max-value` and it represents an alarm if the latest stock price is above this value.
- get the stock names and values from `twelvedata`.
- the api key should be in the `api-key.js` file.
- create a `.gitignore` to exclude `api-key.js`.
- the `ticker` should support suggestions with the names from `twelvedata` api endpoint `/stocks`.
- after the user fills the `ticker` field, and presses `enter` key, check if it matches a stock name, if it does, get the stock values.
- after retrieving the stock prices, plot a candlestick chart.
- each candlestick has at the top `high` value, at the bottom the `low` value.
- plot a linear chart for the `open` values.
- display an `X` axis, the labels for the date, and `Y` axis, the labels for the stock price.
- if the user clicks on the chart, display a vertical bar at that position and display a label with the date and open,high,low of the stock price.
- the stocks retrieval and chart should be updated every `refresh-rate` minute.
- if `min-value` field is a valid number, and the most recent open is below that value, it should do a `window.alert` instructing the user that the value is below `min-value`.
- if `max-value` field is a valid number, and the most recent open is above that value, it should do a `window.alert` instructing the user that the value is above `max-value`.

Google Gemini response

From this chatbox prompt, Google Gemini generated the following files:

The file structure:

stock-analysis/
├── index.html
├── api-key.js
├── script.js
├── style.css
└── .gitignore

The index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stock Analysis</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T384r9gHJA7C5t2Zrvu1AgMEQf240+iLOaOg938KBLDnZqPBfXVr1tJ+hVOWrBlH+D" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <link rel="stylesheet" href=style.css">
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <input type="text" id="ticker" class="form-control d-inline ms-2" max-width="200px" placeholder="Ticker">
                <input type="number" id="refresh-rate" class="form-control d-inline ms-2" max-width="200px" value="5">
                <input type="number" id="min-value" class="form-control d-inline ms-2" max-width="200px" placeholder="Min value">
                <input type="number" id="max-value" class="form-control d-inline ms-2" max-width="200px" placeholder="Max value">
            </div>
            <div class="col-md-9">
                <canvas id="myChart"></canvas>
            </div>
        </div>
    </div>
    <script src="api-key.js"></script>
    <script src="script.js"></script>
</body>
</html>

The style.css file:
/* Add your custom styles here */

The .gitignore file:
api-key.js

The api-key.js file:
const apiKey = 'YOUR_TWELVEDATA_API_KEY';

The script.js file:
$(document).ready(function () {
    const apiKey = window.apiKey; // Access API key from api-key.js
    const chartCanvas = document.getElementById('myChart');
    let chart;

    $('#ticker').keypress(function (e) {
        if (e.which === 13) { // Enter key pressed
            const ticker = $(this).val().toUpperCase();
            getStockData(ticker);
        }
    });

    function getStockData(ticker) {
        $.ajax({
            url: `https://api.twelvedata.com/time_series?symbol=${ticker}&interval=1day&apikey=${apiKey}`,
            method: 'GET',
            success: function (data) {
                // Handle successful response
                const stockData = data.values.map(value => ({
                    time: new Date(value.datetime),
                    open: parseFloat(value.open),
                    high: parseFloat(value.high),
                    low: parseFloat(value.low),
                    close: parseFloat(value.close)
                }));

                createChart(stockData);
                startRefreshInterval();
            },
            error: function (error) {
                // Handle error
                console.error('Error fetching stock data:', error);
            }
        });
    }

    function createChart(stockData) {
        if (chart) {
            chart.destroy();
        }

        chart = new Chart(chartCanvas, {
            type: 'candlestick',
            data: {
                datasets: [{
                    label: 'Stock Price',
                    data: stockData,
                    backgroundColor: 'rgba(255, 99, 132, 0.2)',
                    borderColor: 'rgba(255, 99, 132, 1)',
                    borderWidth: 1
                }]
            },
            options: {
                // Chart configuration options
                scales: {
                    x: {
                        type: 'time',
                        time: {
                            unit: 'day'
                        }
                    },
                    y: {
                        beginAtZero: true
                    }
                },
                plugins: {
                    tooltip: {
                        callbacks: {
                            label: function (context) {
                                const label = context.dataset.label || '';
                                const dataPoint = context.parsed;
                                return `${label}: ${dataPoint.y}`;
                            }
                        }
                    },
                    annotation: {
                        annotations: {
                            verticalLine: {
                                type: 'line',
                                scaleID: 'x',
                                xValue: null,
                                borderColor: 'rgba(255, 99, 132, 1)',
                                borderWidth: 2
                            }
                        }
                    },
                }
            },
        });
        chart.options.plugins.annotation.annotations.verticalLine.label = {
            enabled: false,
            content: 'Loading...',
            position: 'top'
        };

        chart.options.onClick = function (event, active) {
            const index = active[0].index;
            const dataPoint = chart.data.datasets[0].data[index];
            chart.options.plugins.annotation.annotations.verticalLine.xValue = dataPoint.time;
            chart.options.plugins.annotation.annotations.verticalLine.label.content = `Date: ${dataPoint.time.toLocaleDateString()}<br>Open: ${dataPoint.open}<br>High: ${dataPoint.high}<br>Low: ${dataPoint.low}`;
            chart.options.plugins.annotation.annotations.verticalLine.label.enabled = true;
            chart.update();
        };
    }

    function startRefreshInterval() {
        const refreshRate = parseInt($('#refresh-rate').val());
        setInterval(function () {
            getStockData($('#ticker').val());
        }, refreshRate * 60 * 1000);
        const minValue = parseFloat($('#min-value').val());
        const maxValue = parseFloat($('#max-value').val());
        if (!isNaN(minValue) && stockData[stockData.length - 1].open < minValue) {
            window.alert(`Stock price is below the minimum value of ${minValue}`);
        }
        if (!isNaN(maxValue) && stockData[stockData.length - 1].open > maxValue) {
            window.alert(`Stock price is above the maximum value of ${maxValue}`);
        }
    }
});

Google Gemini response issues

The first Google Gemini response didn’t provide a complete script.js code, I had to ask to complete it, in order to get more code, and then merge the code with the previous code.
After having the entire javascript code, I changed the value of api-key.js to my apiKey.
Once I view the webpage on a web browser, I found the following  issues:

  • 👎️ The href on the index.html file doesn’t start with ./, therefore it only works on a website root. I tweak it manually:

<script src="./api-key.js"></script>
<script src="./script.js"></script>

  • 👎️ The apiKey on api-key.js starts with const, with this const, it won’t be accessible to script.js. It worked, after changing to:

apiKey = 'YOUR_TWELVEDATA_API_KEY';

  • 👎️ The line with const chartCanvas = document.getElementById('myChart'); misses the  .getContext('2d'); at the end.
    It should be:

const chartCanvas = document.getElementById('myChart').getContext('2d');

After these changes above, there was still no chart being displayed.
I went to dig up the documentation code of chartjs, and I discovered that it didn’t support the candlestick type.
Instead of thinking that the output was a complete hallucinogenic response of Google Gemini, or spending an immense time researching, I gave it a pause, and tried to use ChatGPT chatbox, this later added an extra external script: chartjs-chart-financial.

  • 👎️ The package chartjs which doesn’t support candlestick type, but the npm package plugin chartjs-chart-financial adds this functionality.
    Therefore, I added this functionality to the html file:

<script src="https://cdn.jsdelivr.net/npm/chartjs-chart-financial"></script>

I continued my attempts on the ChatGPT and after being able to see a graph plot, even not coherent with what I wanted, it gave me the insight to quickly determine the problem.
From the ChatGPT attempt, I was able to find the correct external scripts, as described in ChatGPT chatbox response issues.
After all these changes, it wasn’t still able to produce a simple chart.
I compared with ChatGPT results, and I discovered these script lines were incorrect:
const stockData = data.values.map(value => ({
    time: new Date(value.datetime),
    open: parseFloat(value.open),
    high: parseFloat(value.high),
    low: parseFloat(value.low),
    close: parseFloat(value.close)
}));

It should be:
const stockData = data.values.map(value => ({
    x: new Date(value.datetime),
    o: parseFloat(value.open),
    h: parseFloat(value.high),
    l: parseFloat(value.low),
    c: parseFloat(value.close)
}));

Finally, I was able to see a chart being plotted. It wasn’t what I expected but at this point it was enough to have a better notion of the capabilities of Google Gemini code generation.

Google Gemini manually tweaked response

The end result after the previous tweaks.
 index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stock Analysis</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T384r9gHJA7C5t2Zrvu1AgMEQf240+iLOaOg938KBLDnZqPBfXVr1tJ+hVOWrBlH+D" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1"></script>
    <script src="https://www.chartjs.org/chartjs-chart-financial/chartjs-chart-financial.js" type="text/javascript"></script>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <input type="text" id="ticker" class="form-control d-inline ms-2" max-width="200px" placeholder="Ticker">
                <input type="number" id="refresh-rate" class="form-control d-inline ms-2" max-width="200px" value="5">
                <input type="number" id="min-value" class="form-control d-inline ms-2" max-width="200px" placeholder="Min value">
                <input type="number" id="max-value" class="form-control d-inline ms-2" max-width="200px" placeholder="Max value">
            </div>
            <div class="col-md-9">
                <canvas id="myChart"></canvas>
            </div>
        </div>
    </div>
    <script src="./api-key.js"></script>
    <script src="./script.js"></script>
</body>
</html>

The script.js file:
$(document).ready(function () {
    const apiKey = window.apiKey; // Access API key from api-key.js
    const chartCanvas = document.getElementById('myChart').getContext('2d');
    let chart;

    $('#ticker').keypress(function (e) {
        if (e.which === 13) { // Enter key pressed
            const ticker = $(this).val().toUpperCase();
            getStockData(ticker);
        }
    });

    function getStockData(ticker) {
        $.ajax({
            url: `https://api.twelvedata.com/time_series?symbol=${ticker}&interval=1day&apikey=${apiKey}`,
            method: 'GET',
            success: function (data) {
                // Handle successful response
                const stockData = data.values.map(value => ({
                    x: new Date(value.datetime),
                    o: parseFloat(value.open),
                    h: parseFloat(value.high),
                    l: parseFloat(value.low),
                    c: parseFloat(value.close)
                }));

                createChart(stockData);
                startRefreshInterval();
            },
            error: function (error) {
                // Handle error
                console.error('Error fetching stock data:', error);
            }
        });
    }

    function createChart(stockData) {
        if (chart) {
            chart.destroy();
        }

        chart = new Chart(chartCanvas, {
            type: 'candlestick',
            data: {
                datasets: [{
                    label: 'Stock Price',
                    data: stockData,
                    backgroundColor: 'rgba(255, 99, 132, 0.2)',
                    borderColor: 'rgba(255, 99, 132, 1)',
                    borderWidth: 1
                }]
            },
            options: {
                // Chart configuration options
                scales: {
                    x: {
                        type: 'time',
                        time: {
                            unit: 'day'
                        }
                    },
                    y: {
                        beginAtZero: true
                    }
                },
                plugins: {
                    tooltip: {
                        callbacks: {
                            label: function (context) {
                                const label = context.dataset.label || '';
                                const dataPoint = context.parsed;
                                return `${label}: ${dataPoint.y}`;
                            }
                        }
                    },
                    annotation: {
                        annotations: {
                            verticalLine: {
                                type: 'line',
                                scaleID: 'x',
                                xValue: null,
                                borderColor: 'rgba(255, 99, 132, 1)',
                                borderWidth: 2
                            }
                        }
                    },
                }
            },
        });
        chart.options.plugins.annotation.annotations.verticalLine.label = {
            enabled: false,
            content: 'Loading...',
            position: 'top'
        };

        chart.options.onClick = function (event, active) {
            const index = active[0].index;
            const dataPoint = chart.data.datasets[0].data[index];
            chart.options.plugins.annotation.annotations.verticalLine.xValue = dataPoint.time;
            chart.options.plugins.annotation.annotations.verticalLine.label.content = `Date: ${dataPoint.time.toLocaleDateString()}<br>Open: ${dataPoint.open}<br>High: ${dataPoint.high}<br>Low: ${dataPoint.low}`;
            chart.options.plugins.annotation.annotations.verticalLine.label.enabled = true;
            chart.update();
        };
    }

    function startRefreshInterval() {
        const refreshRate = parseInt($('#refresh-rate').val());
        setInterval(function () {
            getStockData($('#ticker').val());
        }, refreshRate * 60 * 1000);
        const minValue = parseFloat($('#min-value').val());
        const maxValue = parseFloat($('#max-value').val());
        if (!isNaN(minValue) && stockData[stockData.length - 1].open < minValue) {
            window.alert(`Stock price is below the minimum value of ${minValue}`);
        }
        if (!isNaN(maxValue) && stockData[stockData.length - 1].open > maxValue) {
            window.alert(`Stock price is above the maximum value of ${maxValue}`);
        }
    }
});

Google Gemini final webpage

The picture below is the result after all the previous issues fixed:

ChatGPT 4o

This time, I tried using ChatGPT to achieve the same result.
I did a small tweak to the prompt:
Instead of asking the apiKey of being in an external file, it’s better for demo purposes to have a password input field, this removes the burden of having to manage a sensitive file.

create `html` and `javascript` website:
- use bootstrap 5 and jquery.
- it should have the following input files at the left top using css classes: `form-control d-inline ms-2` and `max-width: 200px`.
  - The first input field is `ticker` and it represents a stock ticker.
  - The second field should be `refresh-rate` and it represents the time to retrieve the stocks. it should be filled with number 5.
  - The third field is `min-value` and it represents an alarm if the latest stock price is under this value.
  - The fourth field is `max-value` and it represents an alarm if the latest stock price is above this value.
  - The fifth field of password type is `api-key` and it represents the `twelvedata` api key.
- get the stock names and values from `twelvedata`.
- the `ticker` should support suggestions with the names from `twelvedata` api endpoint `/stocks`.
- after the user fills the `ticker` field, and presses `enter` key, check if it matches a stock name, if it does, get the stock values.
- after retrieving the stock prices, plot a `candlestick` type chart.
- each candlestick has at the top `high` value, at the bottom the `low` value.
- plot a linear chart for the `open` values.
- display an `X` axis, the labels for the date, and `Y` axis, the labels for the stock price.
- if the user clicks on the chart, display a vertical bar at that position and display a label with the date and open,high,low of the stock price.
- the stocks retrieval and chart should be updated every `refresh-rate` minute.
- if `min-value` field is a valid number, and the most recent open is below that value, it should do a `window.alert` instructing the user that the value is below `min-value`.
- if `max-value` field is a valid number, and the most recent open is above that value, it should do a `window.alert` instructing the user that the value is above `max-value`.

ChatGPT chatbox response

Just like Google Gemini it provided a partial javascript implementation, but instead of asking for remaining, I just wanted to see a working solution.
Therefore, the response below is less  complete than the previous one.

The index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stock Price Monitor</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .max-width-200 {
            max-width: 200px;
        }
    </style>
</head>
<body>
    <div class="container mt-3">
        <div class="row">
            <div class="col">
                <input type="text" id="ticker" class="form-control d-inline ms-2 max-width-200" placeholder="Ticker">
                <input type="number" id="refresh-rate" class="form-control d-inline ms-2 max-width-200" placeholder="Refresh Rate" value="5">
                <input type="number" id="min-value" class="form-control d-inline ms-2 max-width-200" placeholder="Min Value">
                <input type="number" id="max-value" class="form-control d-inline ms-2 max-width-200" placeholder="Max Value">
                <input type="password" id="api-key" class="form-control d-inline ms-2 max-width-200" placeholder="API Key">
            </div>
        </div>
        <div class="row mt-3">
            <div class="col">
                <div id="chart-container"></div>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-chart-financial/dist/chartjs-chart-financial.min.js"></script>
</body>
</html>


The script.js file:
<script>
$(document).ready(function() {
    const apiKey = $('#api-key').val();
    const refreshRate = $('#refresh-rate').val() * 60 * 1000;
    let intervalId;

    $('#ticker').on('keypress', function(e) {
        if (e.which === 13) {
            const ticker = $(this).val();
            checkTicker(ticker);
        }
    });

    function checkTicker(ticker) {
        $.ajax({
            url: `https://api.twelvedata.com/stocks`,
            method: 'GET',
            data: { symbol: ticker, apikey: apiKey },
            success: function(response) {
                if (response.data.length > 0) {
                    startRetrievingStockData(ticker);
                } else {
                    alert('Ticker not found.');
                }
            }
        });
    }

    function startRetrievingStockData(ticker) {
        clearInterval(intervalId);
        retrieveStockData(ticker);
        intervalId = setInterval(() => retrieveStockData(ticker), refreshRate);
    }

    function retrieveStockData(ticker) {
        $.ajax({
            url: `https://api.twelvedata.com/time_series`,
            method: 'GET',
            data: {
                symbol: ticker,
                interval: '1min',
                apikey: apiKey
            },
            success: function(response) {
                if (response.status === 'ok') {
                    plotChart(response.values);
                    checkAlarms(response.values[0].open);
                } else {
                    alert('Failed to retrieve stock data.');
                }
            }
        });
    }

    function plotChart(data) {
        const ctx = document.getElementById('chart-container').getContext('2d');
        const candlestickData = data.map(item => ({
            x: new Date(item.datetime),
            o: parseFloat(item.open),
            h: parseFloat(item.high),
            l: parseFloat(item.low),
            c: parseFloat(item.close)
        }));

        const chart = new Chart(ctx, {
            type: 'candlestick',
            data: {
                datasets: [{
                    label: 'Stock Data',
                    data: candlestickData
                }]
            },
            options: {
                scales: {
                    x: {
                        type: 'time',
                        time: {
                            unit: 'minute'
                        }
                    },
                    y: {
                        beginAtZero: false
                    }
                }
            }
        });
    }

    function checkAlarms(latestOpen) {
        const minValue = $('#min-value').val();
        const maxValue = $('#max-value').val();

        if (minValue && latestOpen < minValue) {
            window.alert(`The latest open value ${latestOpen} is below the minimum value ${minValue}`);
        }
        if (maxValue && latestOpen > maxValue) {
            window.alert(`The latest open value ${latestOpen} is above the maximum value ${maxValue}`);
        }
    }
});
</script>

ChatGPT chatbox response issues

Unlike Google Gemini which provided a file structure, and the html code separated from the javascript file, ChatGPT generated two files .html and .js which require to insert the javascript inside the .html file since it’s wrapped with a <script> tags.
Comparing to Google Gemini:

  • 👍️ It included the chartjs-chart-financial script, which is probably the hardest part to detect.
  • 👍️ It included the .getContext('2d') on the document.getElementById('chart-container').

Where did it failed (both issues were fixed on ChatGPT 5:

  • 👎️ Gets the value of the apiKey right on $(document).ready not giving time for the user to input the api-key. Fixed on ChatGPT 5.
  • 👎️ Uses <div> for chart-container which doesn’t allows to .getContext(, instead, it should have used a <canvas>. Fixed on ChatGPT 5.


Although it provided the external script  chartjs-chart-financial, I had to dig into the chartjs-chart-financial example to understand the problem.
The correct scripts were:

<script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1"></script>
<script src="https://www.chartjs.org/chartjs-chart-financial/chartjs-chart-financial.js" type="text/javascript"></script>

After these fixes, it was able to produce a candlestick chart.
It was far and beyond what I requested, it would require more research to make visual results useful, but, at this point, it was clear the limitation of both AI tools, so I decided to halt the process.

ChatGPT manually tweaked response

The end result in a single .html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stock Price Monitor</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .max-width-200 {
            max-width: 200px;
        }
    </style>
</head>
<body>
    <div class="container mt-3">
        <div class="row">
            <div class="col">
                <input type="text" id="ticker" class="form-control d-inline ms-2 max-width-200" placeholder="Ticker">
                <input type="number" id="refresh-rate" class="form-control d-inline ms-2 max-width-200" placeholder="Refresh Rate" value="5">
                <input type="number" id="min-value" class="form-control d-inline ms-2 max-width-200" placeholder="Min Value">
                <input type="number" id="max-value" class="form-control d-inline ms-2 max-width-200" placeholder="Max Value">
                <input type="password" id="api-key" class="form-control d-inline ms-2 max-width-200" placeholder="API Key">
            </div>
        </div>
        <div class="row mt-3">
            <div class="col">
                <canvas id="chart-container"></canvas>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1"></script>
    <script src="https://www.chartjs.org/chartjs-chart-financial/chartjs-chart-financial.js" type="text/javascript"></script>

    <script>
        $(document).ready(function() {
            const apiKey = $('#api-key').val();
            const refreshRate = $('#refresh-rate').val() * 60 * 1000;
            let intervalId;

            $('#ticker').on('keypress', function(e) {
                if (e.which === 13) {
                    const ticker = $(this).val();
                    checkTicker(ticker);
                }
            });

            function checkTicker(ticker) {
                const apiKey = $('#api-key').val();
                $.ajax({
                    url: `https://api.twelvedata.com/stocks`,
                    method: 'GET',
                    data: { symbol: ticker, apikey: apiKey },
                    success: function(response) {
                        if (response.data.length > 0) {
                            startRetrievingStockData(ticker);
                        } else {
                            alert('Ticker not found.');
                        }
                    }
                });
            }

            function startRetrievingStockData(ticker) {
                clearInterval(intervalId);
                retrieveStockData(ticker);
                intervalId = setInterval(() => retrieveStockData(ticker), refreshRate);
            }

            function retrieveStockData(ticker) {
                const apiKey = $('#api-key').val();
                $.ajax({
                    url: `https://api.twelvedata.com/time_series`,
                    method: 'GET',
                    data: {
                        symbol: ticker,
                        interval: '1min',
                        apikey: apiKey
                    },
                    success: function(response) {
                        if (response.status === 'ok') {
                            plotChart(response.values);
                            checkAlarms(response.values[0].open);
                        } else {
                            alert('Failed to retrieve stock data.');
                        }
                    }
                });
            }

            function plotChart(data) {
                const ctx = document.getElementById('chart-container').getContext('2d');
                const candlestickData = data.map(item => ({
                    x: new Date(item.datetime),
                    o: parseFloat(item.open),
                    h: parseFloat(item.high),
                    l: parseFloat(item.low),
                    c: parseFloat(item.close)
                }));

                const chart = new Chart(ctx, {
                    type: 'candlestick',
                    data: {
                        datasets: [{
                            label: 'Stock Data',
                            data: candlestickData
                        }]
                    },
                    options: {
                        scales: {
                            x: {
                                type: 'time',
                                time: {
                                    unit: 'minute'
                                }
                            },
                            y: {
                                beginAtZero: false
                            }
                        }
                    }
                });
            }

            function checkAlarms(latestOpen) {
                const minValue = $('#min-value').val();
                const maxValue = $('#max-value').val();

                if (minValue && latestOpen < minValue) {
                    window.alert(`The latest open value ${latestOpen} is below the minimum value ${minValue}`);
                }
                if (maxValue && latestOpen > maxValue) {
                    window.alert(`The latest open value ${latestOpen} is above the maximum value ${maxValue}`);
                }
            }
        });
        </script>
</body>
</html>

ChatGPT final webpage

The picture below is the result after all the previous issues fixed:

ChatGPT 5

The unofficial version of ChatGPT 5 became available after the initial release of the article.
The code generated on the new version no longer has the problem with the <canvas> and apiKey described in the section above, however, still wasn’t able to produce a fully functional candlestick chart.

Conclusion

  • 👎️ None of the AI tools used, Google Gemini and ChatGPT, were able to provide a working solution. Even after multiple fixes, the final result was still garbled with the charts providing unusable results.
  • 👍️ ChatGPT added the link to chartjs-chart-financial, although it wasn’t working, it was a fundamental point since chartjs doesn’t support candlestick chart type. Using only Google Gemini, the user would believe that the output was completely useless, while with this link, after some research, I was able to display something on the chart.
  • 👍 Both AI tools were able to retrieve the stock names and prices from “twelvedata” since their API is well documented.
  • 👍 Both AI tools were able to generate well-formed html and javascript code.
  • 👍 Both AI tools were able to generate basic chart code, which could have been due chartjs.org provides documentation and examples.
  • 👎️ Both AI tools didn’t produce a correct candlestick chart, which could have been due the chartjs-chart-financial plugin doesn’t provide proper documentation.


In comparison, ChatGPT produced less errors with a single prompt and a better solution than Google Gemini.