计算没有价格变化的非交易日/天的数量

时间:2014-05-06 09:16:39

标签: sql postgresql finance

我有一张关于债券收盘价的表格,其基本结构如下:

     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;

有更好的方法吗?

1 个答案:

答案 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;

有一点需要注意的是,这可能无法按照你想要的方式处理边界条件。