Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Querying PostgreSQL for Open-High-Low-Close (OHLC) report

I am trying to query a table in PostgreSQL 8.4.2 server for to get open-high-low-close data. The table and my first query are below.

Question: Is there a way to get the same results without using subqueries as in the example query below? Perhaps using FIRST_VALUE() or LAST_VALUE() window methods?

-- FIRST ATTEMPT AT OHLC
SELECT
  contract_id
, TO_CHAR(ts, 'YYMMDDHH24MI')
, (SELECT price FROM fill minF WHERE minF.fill_id = MIN(f.fill_id)) AS open
, MAX(f.price) AS high
, MIN(f.price) AS low
, (SELECT price FROM fill maxF WHERE maxF.fill_id = MAX(f.fill_id)) AS close
FROM fill f
GROUP BY 1,2
ORDER BY 1,2;

-- SIMPLIFIED DDL
CREATE TABLE fill
(
    contract_id SEQUENCE PRIMARY KEY
,   ts          TIMESTAMP
,   price       NUMERIC(10,4)
);
like image 311
ravi77o Avatar asked Jan 23 '23 08:01

ravi77o


2 Answers

You have yours grouped down to the minute. Which I'm going to assume is wrong because these are typically done by day. If I'm wrong, you'll have to change it back.

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
like image 164
Scott Bailey Avatar answered Feb 04 '23 05:02

Scott Bailey


I would like to get sub-day resolution. This appears to work well.

SELECT
  contract_id
, the_minute
, open
, high
, low
, close
FROM 
(
  SELECT
    contract_id
  , TO_CHAR(ts, 'YYMMDDHH24MI') AS the_minute
  , MIN(price) OVER w            AS low
  , MAX(price) OVER w            AS high
  , LAST_VALUE(price) OVER w     AS open   -- Note the window is in reverse (first value comes last)
  , FIRST_VALUE(price) OVER w    AS close  -- Note the window is in reverse (last value comes first)
  , RANK() OVER w                AS the_rank
  FROM fill
  WINDOW w AS (PARTITION BY contract_id, TO_CHAR(ts, 'YYMMDDHH24MI') ORDER BY fill_id DESC)
) AS inr
WHERE the_rank = 1
ORDER BY 1, 2;

Thank you, Scott. You answer helped me get to the following solution.

like image 32
ravi77o Avatar answered Feb 04 '23 04:02

ravi77o