查询PostgreSQL的Open-High-Low-Close(OHLC)报告

时间:2010-03-29 20:16:48

标签: sql postgresql finance

我正在尝试查询PostgreSQL 8.4.2服务器中的表以获取开 - 高 - 低 - 关闭数据。该表和我的第一个查询如下。

问题: 有没有办法在不使用子查询的情况下获得相同的结果,如下面的示例查询中所示?也许使用FIRST_VALUE()或LAST_VALUE()窗口方法?

-- 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)
);

4 个答案:

答案 0 :(得分:2)

我想得到分日决议。这似乎运作良好。

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;
谢谢你,斯科特。你的回答帮助我找到了以下解决方案。

答案 1 :(得分:2)

我发现Timescale extension的PostgresSQL非常方便。它具有按任意时间间隔分组的功能。该函数称为time_bucket(),并且具有与内置date_trunc()函数相同的语法,但是采用间隔而不是时间精度作为第一个参数。无需使用窗口/分区。 Here,您可以找到其API文档。这是一个示例:

SELECT  
    time_bucket('1 minute', timestamp_) timebucket,
    (array_agg(price ORDER BY timestamp_ ASC))[1] open,
    MAX(price) high,
    MIN(price) low,
    (array_agg(price ORDER BY timestamp_ DESC))[1] close,
    SUM(turnover) turnover,
    COUNT(*) nr_ticks
FROM price_tick
GROUP BY timebucket
ORDER BY timebucket

如果您要使用新的摄取数据自动更新“按时间间隔分组” 视图,并且还想查询这些视图,还可以查看continuous aggregate views频繁地这样可以节省大量资源,并使查询更快。

答案 2 :(得分:1)

你有你的分组到分钟。我将假设这是错误的,因为这些通常是在白天完成的。如果我错了,你将不得不改回来。

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

答案 3 :(得分:0)

我是这样解决的:

select FLOOR(MIN(ts) / :period) * :period                    as timestamp,
       SUBSTRING_INDEX(MIN(CONCAT(ts, '_', price)), '_', -1) as open,
       max(price)                                            as high,
       min(price)                                            as low,
       SUBSTRING_INDEX(MAX(CONCAT(ts, '_', price)), '_', -1) as close,
       sum(amount)                                           as volume
from uni_tx
where asset = :asset
  and ts between :startTime and :endTime
GROUP BY FLOOR(ts / :period)
order by timestamp