我有一个包含以下架构的财务数据表:
Table "public.candles"
Column | Type | Modifiers
------------+----------------+-----------
posix_time | bigint | not null
low | numeric(8,2) | not null
high | numeric(8,2) | not null
open | numeric(8,2) | not null
close | numeric(8,2) | not null
volume | numeric(23,16) | not null
Indexes:
"candles_pkey" PRIMARY KEY, btree (posix_time)
每支蜡烛间隔一分钟。我想将数据汇总成蜡烛,间隔时间为5分钟,1小时,1天等等。
我可以用五分钟的间隔汇总posix_time
,high
,low
和volume
SELECT posix_time/(60*5)*(60*5) AS new_posix_time,
max(high) AS new_high,
min(low) AS new_low,
sum(volume) AS new_volume
FROM candles
GROUP BY new_posix_time
并使用适当的
变体计算新的open
和close
值
SELECT posix_time/(60*5)*(60*5) AS new_posix_time,
open AS new_open
FROM (SELECT open,
posix_time,
ROW_NUMBER() OVER (PARTITION BY posix_time/(60*5)*(60*5)
ORDER BY posix_time ASC) AS r
FROM candles
) AS o
WHERE o.r = 1
如this question中所述,但我无法弄清楚如何将它们合并为一个查询。
我需要使用连接吗?子查询?完全重构查询?
答案 0 :(得分:1)
您可以使用generate_series()
来获取所需的时间范围。然后,您可以使用left join
和聚合。像这样:
select t.ts,
min(low) as low, max(high) as high, sum(volume) as volume
from generate_series('2016-01-01'::timestamp, '2016-01-02'::timestamp, interval '5 minute'
) t(ts) left join
candles c
on '1970-01-01' + c.posix_time * interval '1 second' between t.ts and t.ts + interval '5 minute'
group by t.ts;
编辑:
获得开放和关闭时间需要更多级别的处理:
select ts, min(low) as low, max(high) as high, sum(volume) as volume,
min(open) as open, min(close) as close
from (select t.*, c.*,
first_value(open) over (partition by t.ts order by c.posix_time asc) as open,
first_value(open) over (partition by t.ts order by c.posix_time desc) as close
from generate_series('2016-01-01'::timestamp, '2016-01-02'::timestamp, interval '5 minute'
) t(ts) left join
candles c
on '1970-01-01' + c.posix_time * interval '1 second' between t.ts and t.ts + interval '5 minute'
) t
group by ts;
答案 1 :(得分:0)
我知道你可能不想听到这个,但使用熊猫会让你的生活更轻松......
sqla = ("SELECT posix_time, open, high, low, close FROM table")
df = psql.read_sql(sqla, con)
Timeframe = '5T'
df.resample(Timeframe ).agg({ 'open' : 'first', 'high' : 'max', 'low' : 'min', 'close': 'last'})
答案 2 :(得分:0)
以下是我自己使用自定义聚合函数的解决方案:
CREATE TYPE open_close_agg_type AS (
t bigint,
p numeric(8,2)
);
-- assumes price will never be 0
CREATE OR REPLACE
FUNCTION close_state_func(open_close_agg_type, open_close_agg_type)
RETURNS open_close_agg_type LANGUAGE sql
AS $$
SELECT CASE WHEN ($1).p = 0 THEN $2
WHEN ($2).p = 0 THEN $1
WHEN ($1).t > ($2).t THEN $1
ELSE $2
END
$$;
-- assumes price will never be 0
CREATE OR REPLACE
FUNCTION open_state_func(open_close_agg_type, open_close_agg_type)
RETURNS open_close_agg_type LANGUAGE sql
AS $$
SELECT CASE WHEN ($1).p = 0 THEN $2
WHEN ($2).p = 0 THEN $1
WHEN ($1).t < ($2).t THEN $1
ELSE $2
END
$$;
CREATE OR REPLACE
FUNCTION open_close_agg_finalize_func(open_close_agg_type)
RETURNS numeric(8,2) LANGUAGE sql
AS $$
SELECT ($1).p
$$;
CREATE AGGREGATE last_closing(open_close_agg_type) (
SFUNC=close_state_func,
STYPE=open_close_agg_type,
INITCOND='(0, 0.0)',
FINALFUNC=open_close_agg_finalize_func
);
CREATE AGGREGATE first_opening(open_close_agg_type) (
SFUNC=open_state_func,
STYPE=open_close_agg_type,
INITCOND='(0, 0.0)',
FINALFUNC=open_close_agg_finalize_func
);
运行以下查询:
SELECT posix_time/(60*5)*(60*5) AS new_posix_time,
min(low) AS new_low,
max(high) AS new_high,
first_opening((posix_time, open)) AS new_open,
last_closing((posix_time, close)) AS new_close,
sum(volume) AS new_volume
FROM candles
GROUP BY new_posix_time
我选择戈登的答案,因为它不那么冗长。