【问题标题】:Querying PostgreSQL for Open-High-Low-Close (OHLC) report查询 PostgreSQL 的 Open-High-Low-Close (OHLC) 报告
【发布时间】:2011-02-02 05:07:53
【问题描述】:

我正在尝试查询 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)
);

【问题讨论】:

    标签: sql postgresql finance


    【解决方案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
    

    【讨论】:

    • 感谢您的回答,Scott,但我试图获得比 1 天间隔更高的分辨率。您的查询帮助我更好地理解了窗口化,我想我有一个解决方案。
    • 您可能希望在PARTITION BY 表达式的末尾添加rows between unbounded preceding and unbounded following,或者在窗口函数中添加ORDER BY,您可能会得到一些意想不到的结果。以下答案提供了更多信息:stackoverflow.com/questions/57639840/…
    【解决方案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;
    

    谢谢你,斯科特。您的回答帮助我找到了以下解决方案。

    【讨论】:

    • fill_id 参数从何而来?我实际上正在尝试使此查询适应我的情况,但没有成功
    【解决方案3】:

    我找到了一个非常方便的方法,使用 Timescale extension 用于 PostgresSQL。它提供了按任意时间间隔分组的能力。该函数称为time_bucket(),与内置date_trunc() 函数具有相同的语法,但将间隔而不是时间精度作为第一个参数。无需使用窗口/分区。 Here 你可以找到它的 API Docs。这是一个例子:

    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基础。这可以为您节省大量资源,并使您的查询速度更快。

    【讨论】:

    • 只是好奇,turnover 是什么?是 1 分钟的交易量除以流通股吗?
    • @dvtan 在这种情况下,营业额描述了在特定时间间隔内交易的货币金额。如果您以 50 美元的价格购买 10 股,那么您的营业额为 500 美元。该交易将代表数据库中的一行,其中包含价格和相应的营业额(当然您也可以只存储交易股票的数量并计算每个时间间隔的营业额)。
    【解决方案4】:

    我就是这样解决的:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-02
      • 1970-01-01
      • 2020-01-23
      • 1970-01-01
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多