Question:
I could not find an issue like this one on SO. I have seen the following:- R/SQL – Portfolio Performance
- SQL Server : Group By Price and Sum Amount
- SQL Query Balance
- Sql query to find total balance
None of them help me with the issue I have.
Lets say that I have a system that keeps track of my portfolio of shares. I have two main tables. one with the buy and sell orders that I have made, and the other with share prices for many shares over multiple years.
I am looking for an efficient script that gives me the overall portfolio dollar value for each order I have made over time. The current implementation I am using is far too slow: it executes a sub-query for every order I have. I can sometimes have tens of thousands of orders in the table.
The SQL
I am running this on an Oracle database:
portfolio_balance_dollars
. One with less runtime complexity.Alternative
An efficient answer for the above would be first prize. But instead of looking up the share prices of all the relevant shares on each order, it also suffices to use the latest share price for the relevant share found within
summary
itself. This is fine because there are orders taking place quite often. So the following output will also be good enough:summary
itself.db<>fiddle
Answer:
You can use:SELECT *
FROM (
SELECT t.*,
SUM(balance_dollars) OVER (PARTITION BY dt)
AS portfolio_balance_dollars
FROM (
SELECT t.ticker,
o.dt,
p.price AS share_price,
o.shares AS order_shares,
o.shares * p.price AS order_dollars,
SUM(o.shares) OVER (PARTITION BY t.ticker ORDER BY o.dt)
AS balance_shares,
SUM(o.shares) OVER (PARTITION BY t.ticker ORDER BY o.dt)
* p.price AS balance_dollars
FROM ( SELECT DISTINCT ticker FROM orders ) t
LEFT OUTER JOIN (
SELECT ticker,
dt,
SUM(shares) AS shares
FROM orders
GROUP BY ticker, dt
) o
PARTITION BY (o.dt)
ON (t.ticker = o.ticker)
CROSS JOIN LATERAL (
SELECT p.*
FROM prices p
WHERE p.ticker = t.ticker
AND p.dt <= o.dt
ORDER BY p.dt DESC
FETCH FIRST ROW ONLY
) p
ORDER BY
o.dt,
t.ticker,
share_price
) t
)
WHERE order_shares IS NOT NULL;
[/code]
TICKER DT SHARE_PRICE ORDER_SHARES ORDER_DOLLARS BALANCE_SHARES BALANCE_DOLLARS PORTFOLIO_BALANCE_DOLLARS AAPL 02-JAN-22 1.1 1000 1100 1000 1100 2150 GOOG 02-JAN-22 10.5 100 1050 100 1050 2150 AAPL 04-JAN-22 1.3 -150 -195 850 1105 2115 AAPL 05-JAN-22 1.1 -100 -110 750 825 1946.1 GOOG 05-JAN-22 11.1 1 11.1 101 1121.1 1946.1
db<>fiddle here
If you have better answer, please add a comment about this, thank you!