When you want to create Candlestick charts you need to group time series into time-frames like: Give me the highest, lowest and average value for each 1 hour of a day. To do so, you can use this native PostgreSQL query:

select
        min(cp.created) as created_min,
        max(cp.created) as created_max,
        cp.symbol_id,
        avg(cp.price) price_avg,
        max(cp.price) price_max,
        min(cp.price) price_min
from
    generate_series(current_timestamp::timestamp-interval '7' day, current_timestamp::timestamp, interval '1' hour) as d(dt)
        join prices cp on (cp.created >= d.dt and cp.created < d.dt + interval '1' hour)

where cp.symbol_id = 'LHA.DE'
group by d.dt, cp.symbol_id
order by d.dt;

You will get a result like

2024-09-16 09:00:00.000000 +00:00,2024-09-16 09:25:00.000000 +00:00,LHA.DE,5.8473333517710367,5.8520002365112305,5.8420000076293945
2024-09-16 09:30:00.000000 +00:00,2024-09-16 10:25:00.000000 +00:00,LHA.DE,5.8581666946411132,5.872000217437744,5.840000152587891
2024-09-16 10:30:00.000000 +00:00,2024-09-16 11:25:00.000000 +00:00,LHA.DE,5.8568333784739175,5.872000217437744,5.8480000495910645
2024-09-16 11:30:00.000000 +00:00,2024-09-16 12:25:00.000000 +00:00,LHA.DE,5.8531666994094847,5.868000030517578,5.843999862670898
2024-09-16 12:30:00.000000 +00:00,2024-09-16 13:25:00.000000 +00:00,LHA.DE,5.8460001078518953,5.8520002365112305,5.840000152587891
2024-09-16 13:30:00.000000 +00:00,2024-09-16 14:25:00.000000 +00:00,LHA.DE,5.8504999876022337,5.869999885559082,5.843999862670898
2024-09-16 14:30:00.000000 +00:00,2024-09-16 15:25:00.000000 +00:00,LHA.DE,5.8638333479563393,5.868000030517578,5.855999946594238
2024-09-16 15:30:00.000000 +00:00,2024-09-16 16:25:00.000000 +00:00,LHA.DE,5.8645000060399373,5.875999927520752,5.8480000495910645
2024-09-16 16:30:00.000000 +00:00,2024-09-16 17:25:00.000000 +00:00,LHA.DE,5.8793332974116008,5.888000011444092,5.861999988555908
2024-09-16 17:35:00.000000 +00:00,2024-09-16 17:35:00.000000 +00:00,LHA.DE,5.886,5.886,5.886
...

But for typical candlesticks, you need to know the first and the last value of each group as well. This feature is available in extensions like TimescaleDB, or you take the native plugin extension postgresql-first-last-aggregate on GitHub, to achieve this with vanilla PostgreSQL without downsides in performance.

Just load the first_last_aggregate.sql into your scheme and extend the query above by the two lines:

first(cp.price) price_open,
last(cp.price) price_close,