2016-06-09 3 views
4

Ich habe eine Tabelle von Finanzdaten mit folgendem Schema:Wie schreibe ich eine Abfrage für die Aggregation von Forex Candle-Daten?

  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) 

Jede Kerze erstreckt sich über einen Ein-Minuten-Intervall. Ich möchte die Daten in Kerzen aggregieren Intervallen von 5 Minuten Spanning, 1 Stunde, 1 Tag, usw.

kann ich aggregieren posix_time, high, low und volume über fünf Minuten-Intervallen mit

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 

und berechnet die die neuen open und close Werte mit der entsprechenden Änderung der

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 

wie invorgeschlagen, aber ich kann nicht herausfinden, wie man sie zu einer Abfrage kombiniert.

Muss ich Joins verwenden? Unterabfragen? Die Abfrage komplett neu strukturieren?

Antwort

1

Sie können generate_series() machen verwenden Sie den Zeitraum der Sie suchen zu erhalten. Dann können Sie left join und Aggregation verwenden. Etwas wie folgt aus:

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; 

EDIT:

die Öffnungs- und Schließzeit bekommen erfordert eine weitere Ebene der Verarbeitung:

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; 
+0

Es scheint, dass dies nur eine alternative Form der ersten Abfrage ist. Wie würde ich das verwenden, um die Spalten "open" und "close" zu aggregieren? – Bryan

0

Ich weiß, Sie wollen wahrscheinlich nicht hören, aber unter Verwendung von Pandas wird Ihr Leben viel einfacher ...

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'}) 
0

Hier ist meine eigene Lösung mit benutzerdefinierten Aggregatfunktionen:

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

Führen Sie die folgende Abfrage aus:

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 

Ich ging mit Gordon's Antwort, weil es weniger wortreich ist.

Verwandte Themen