我有一张关于债券收盘价的表格,其基本结构如下:
bond_id | tdate | price
---------+------------+-------
EIX1923 | 2014-01-01 | 100.12
EIX1923 | 2014-01-02 | 100.10
EIX1923 | 2014-01-05 | 100.10
EIX1923 | 2014-01-10 | 100.15
正如您所看到的,我每天都没有价格 - 因为债券不会每天交易。 我想计算在某一年中这种情况发生的频率,如果债券价格在连续几天之间没有变化,我会将其视为同样的结果。
也就是说,对于N个交易日(不包括周末,忽略假期)的一年,我基本上想要生成一系列日期并计算价格的天数(1)与前一天相同或(2) )当天没有记录并将其除以N.
我正在使用PostgreSQL,所以我开始使用generate_series('2014-01-01'::timestamp, '2015-01-01'::timestamp, '1 day'::interval)
;我可以从这个系列中选择并做一个WHERE来排除周末:
SELECT dd
FROM generate_series(
'2014-01-01'::timestamp,
'2015-01-01'::timestamp,
'1 day'::timestamp
) dd
WHERE EXTRACT(dow FROM dd) NOT IN (0, 6);
现在,我想我想生成一个bond_id
的“列”来加入trade
表格,但我不确定如何。基本上,我认为最简单的结构是LEFT JOIN
,所以我得到类似的东西:
EIX1923 | 2014-01-01 | 100.12
EIX1923 | 2014-01-02 | 100.10
EIX1923 | 2014-01-03 |
EIX1923 | 2014-01-04 |
EIX1923 | 2014-01-05 | 100.10
EIX1923 | 2014-01-06 |
EIX1923 | 2014-01-07 |
EIX1923 | 2014-01-08 |
EIX1923 | 2014-01-09 |
EIX1923 | 2014-01-10 | 100.15
然后我可以用最近可用的价格填补空白,并计算应用程序代码中ABS(∆P) == 0
的数量。但是如果有完全在SQL中做到这一点的解决方案也会很好!我不知道上面的方法是否合适。
(我没有费心去检查2014年1月的第一天是否是周末,因为这只是为了说明;但显然会将它们排除在结果之外。
编辑:似乎可能存在许多类似的问题。希望它不是重复的!
编辑:所以,我用这个玩了一点,这个解决方案在上面意义上“有效”(而且我觉得很快就没有意识到):
SELECT
'EI653670', dd, t.price
FROM
generate_series('2014-01-01'::timestamp, '2015-01-01'::timestamp, '1 day'::interval) dd
LEFT JOIN
trade t ON dd = t.tdate AND t.id = 'EI653670'
WHERE
EXTRACT(dow FROM dd) NOT IN (0, 6) ORDER BY dd;
有更好的方法吗?
答案 0 :(得分:0)
我认为你可以用lag()
来做这个逻辑。以下显示了一般的想法 - 获取上一个日期和价格并做一些逻辑:
select bond_id,
sum(case when prev_price = price
then date - prev_date + 1
when prev_date = date + interval '1 day'
then 0
else date - prev_date
end)
from (select t.*,
lag(t.date) over (partition by t.bond_id order by t.date) as prev_date,
lag(t.price) over (partition by t.bond_id order by t.date) as prev_price
trade t
) t
group by bond_id;
有一点需要注意的是,这可能无法按照你想要的方式处理边界条件。