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
すべてのロジックが正しいかどうかはわかりませんが、少なくとも出発点はあります。
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 ;