web-dev-qa-db-ja.com

注文の要約位置を計算するPostgresウィンドウ関数

Windows関数、CTE、RECURSIVEを試しましたが、それでも頭がおかしくなりません

売買注文の列があり、各取引後のポジションを知りたい場合。

次の注文ごとに既存のポジションが増加している場合-平均価格が欲しいのですが、

注文が減少している場合-ProfitLossを計算し、ポジションの価格をそのままにしておきたい

注文がポジシ​​ョンの方向を変えている場合-ProfitLossとポジションの新しい価格を計算したい

order_profit_loss = LEAST(ABS(qua), lag(ABS(position_qua))) * price
position_price = (LAG(position_price) * LAG(position_qua) - order_profit_loss) / position_qua

DDL

CREATE TABLE trades
(
    id VARCHAR(255) PRIMARY KEY NOT NULL,
    symbol VARCHAR(10) NOT NULL,
    price NUMERIC NOT NULL,
    qua INTEGER NOT NULL,
    dt TIMESTAMP WITH TIME ZONE
);

INSERT INTO public.trades (id, symbol, price, qua, dt) VALUES ('1', 'FB', 124.89, 116, '2016-07-29 23:01:46.334000');
INSERT INTO public.trades (id, symbol, price, qua, dt) VALUES ('2', 'FB', 123.56, -104, '2016-07-30 23:41:13.297000');
INSERT INTO public.trades (id, symbol, price, qua, dt) VALUES ('3', 'FB', 125.23, 20, '2016-07-31 00:20:41.092000');
INSERT INTO public.trades (id, symbol, price, qua, dt) VALUES ('4', 'FB', 123.56, -21, '2016-08-01 12:25:34.482000');

そのような結果を得る方法

id | symbol | price  | qua  |             dt             | order_profit_loss | position_qua | position_price
---+--------+--------+------+----------------------------+-------------------+--------------+----------------
1  | FB     | 124.89 |  116 | 2016-07-29 23:01:46.334+03 |                 0 |          116 |         124.89
2  | FB     | 123.56 | -104 | 2016-07-30 23:41:13.297+03 |           -138.32 |           12 |         124.89
3  | FB     | 125.23 |   20 | 2016-07-31 00:20:41.092+03 |                 0 |           32 |       125.1025
4  | FB     | 123.56 |  -21 | 2016-08-01 12:25:34.482+03 |          -32.3925 |           11 |       125.1025

解決:

CREATE OR REPLACE VIEW v_pos AS
WITH RECURSIVE pos AS
  ( SELECT * FROM (
          SELECT DISTINCT ON (symbol)
               t.*,  0::numeric  AS pnl,
                     qua         AS pos,
                     price       AS p_price
           FROM public.trades AS t
           ORDER BY symbol, dt
         ) AS starting
    UNION ALL
    SELECT
        n.*,
        c.pnl,
        p.pos + n.qua,
        CASE
          WHEN p.pos > 0 AND n.qua > 0 THEN (p.p_price * p.pos + n.price * n.qua) / (p.pos + n.qua)
          WHEN p.pos < 0 AND n.qua < 0 THEN (p.p_price * p.pos + n.price * n.qua) / (p.pos + n.qua)
          WHEN p.pos / n.qua < 0 AND p.pos > n.qua THEN p.p_price
          WHEN p.pos / n.qua < 0 AND p.pos < n.qua THEN n.price
          ELSE 0
        END
    FROM
        pos AS p,
        LATERAL
        ( SELECT t.* FROM trades AS t
          WHERE t.symbol = p.symbol AND t.dt > p.dt ORDER BY t.dt  LIMIT 1
        ) AS n,
        LATERAL (
          SELECT
            CASE
              WHEN p.pos < 0 AND n.qua > 0 AND -p.pos > n.qua THEN n.qua * (p.p_price - n.price)
              WHEN p.pos < 0 AND n.qua > 0 AND -p.pos < n.qua THEN p.pos * (p.p_price - n.price)
              WHEN p.pos > 0 AND n.qua < 0 AND p.pos > -n.qua THEN n.qua * (p.p_price - n.price)
              WHEN p.pos > 0 AND n.qua < 0 AND p.pos < -n.qua THEN p.pos * (p.p_price - n.price)
              ELSE 0
            END pnl,
            p.pos * p.price cb
        ) AS c
  )
SELECT symbol, qua::BIGINT, price FROM (
  SELECT 
    DISTINCT ON (symbol)
    symbol,
    pos qua,
    p_price price
  FROM pos
    WHERE qua != 0
  ORDER BY symbol, dt DESC  
) as tmp
WHERE tmp.qua != 0
1
Andrew Vorobyov

すべてのロジックが正しいかどうかはわかりませんが、少なくとも出発点はあります。

WITH RECURSIVE pos AS
  ( SELECT *
    FROM ( SELECT DISTINCT ON (symbol)
               t.*,  0::numeric  AS order_profit_loss, 
                     qua         AS position_qua, 
                     price       AS position_price 
           FROM public.trades AS t
           ORDER BY symbol, dt 
         ) AS starting
    UNION ALL
    SELECT  
        n.id, n.symbol, n.price, n.qua, n.dt, 
        CASE WHEN n.qua < 0
            THEN n.qua * (p.position_price - n.price)
            ELSE 0
        END                     AS order_profit_loss,
        p.position_qua + n.qua  AS position_qua, 
        CASE WHEN n.qua < 0
            THEN p.position_price
            ELSE (p.position_qua * p.position_price + n.price * n.qua)
                 / (p.position_qua + n.qua)
        END                     AS position_price 
    FROM 
        pos AS p,
      LATERAL
        ( SELECT t.*
          FROM public.trades AS t
          WHERE t.symbol = p.symbol
            AND t.dt > p.dt
          ORDER BY t.dt    LIMIT 1
        ) AS n
  )
SELECT * 
FROM pos
ORDER BY symbol, dt ; 
2
ypercubeᵀᴹ