There are maybe hundreds of ways to achieve it. You could start by creating the quarters you need in a CTE
or Subquery, so that you can compare it to your pricing table, e.g.
Sample data
CREATE TEMPORARY TABLE pricing (id INT, start_date DATE,price NUMERIC);
INSERT INTO pricing VALUES
(5,CURRENT_DATE,1.99),
(4,CURRENT_DATE-2,0.99),
(3,CURRENT_DATE-3,0.42),
(2,CURRENT_DATE-100,0.10),
(1,CURRENT_DATE-121,0.82);
Query
WITH q (year,quarter) AS (
SELECT DISTINCT
EXTRACT(YEAR FROM start_date::TIMESTAMP),
EXTRACT(QUARTER FROM start_date::TIMESTAMP)
FROM pricing
)
SELECT *, (SELECT price
FROM pricing
WHERE EXTRACT(QUARTER FROM start_date::TIMESTAMP) = q.quarter AND
EXTRACT(YEAR FROM start_date::TIMESTAMP) = q.year
ORDER BY start_date LIMIT 1)
FROM q ORDER BY q.year,q.quarter;
year | quarter | price
------+---------+-------
2020 | 4 | 0.82
2021 | 1 | 0.42
Demo: db<>fiddle
EDIT: Depending on your use case you might wanna generate the years and quarters using something like generate_series
rather than running a full scan on pricing
as shown in the example above, e.g.
SELECT
EXTRACT(YEAR FROM d) AS year,
EXTRACT(QUARTER FROM d) AS quarter
FROM generate_series('2019-01-01'::DATE,CURRENT_DATE,'3 month') AS d
ORDER BY 1,2;
year | quarter
------+---------
2019 | 1
2019 | 2
2019 | 3
2019 | 4
2020 | 1
2020 | 2
2020 | 3
2020 | 4
2021 | 1