Masz swoje zgrupowane co do minuty. Zakładam, że jest to błędne, ponieważ zazwyczaj robi się to w ciągu dnia. Jeśli się mylę, będziesz musiał to zmienić z powrotem.
SELECT DISTINCT contract_id, ts::date,
min(price) OVER w,
max(price) OVER w,
first_value(price) OVER w,
last_value(price) OVER w
FROM fill
WINDOW w AS (PARTITION BY contract_id, ts::date ORDER BY ts)
ORDER BY 1,2