FIFO closing price with multiple products and people

霸气de小男生 提交于 2020-01-06 07:02:34

问题


I have found the following solution to a problem I was trying to solve: https://stackoverflow.com/a/26778468/1626443

I want to have a single query that returns me the FIFO price closing price per investor and isin.

The current query works, but I have to filter by the isin and investor (the top is the sample data):

CREATE TABLE #CustomerOrderTable (
    ID INTEGER NOT NULL,
    [investor] VARCHAR(255) NOT NULL,
    [isin] VARCHAR(255) NOT NULL,
    [datetime] DATETIME NOT NULL,
    [quantity] FLOAT NOT NULL,
    [price] FLOAT NOT NULL,
    [buysell] VARCHAR(255) NOT NULL,
    PRIMARY KEY (ID)
);
GO

INSERT INTO #CustomerOrderTable(ID, [investor],[isin],[datetime],[quantity],[price],[buysell]) VALUES (1,'GN0161162','DE6592028664','20180913',7,88,'Buy'),(2,'GN0969461','DE6592028664','20180924',4,452,'Buy'),(3,'GN0969461','DE7478496562','20180929',9,721,'Buy'),(4,'GN0429760','CH1486126118','20180929',9,451,'Buy'),(5,'GN0429760','GB9015779955','20181006',9,817,'Buy'),(6,'GN0969461','DE7478496562','20181013',8,433,'Buy'),(7,'GN0969461','GB9015779955','20181025',10,262,'Buy'),(8,'GN0161162','DE7478496562','20181108',7,278,'Buy'),(9,'GN0429760','GB9015779955','20181109',3,986,'Buy'),(10,'GN0161162','DE6592028664','20181114',1,556,'Buy'),(11,'GN0429760','DE7478496562','20181120',4,434,'Buy'),(12,'GN0429760','GB9015779955','20181121',3,764,'Buy'),(13,'GN0369359','GB9015779955','20181123',7,129,'Buy'),(14,'GN0429760','DE7478496562','20181205',2,686,'Buy'),(15,'GN0969461','DE6592028664','20181231',5,309,'Buy'),(16,'GN0161162','DE9805831979','20190108',3,540,'Buy'),(17,'GN0429760','CH1486126118','20190128',4,940,'Buy'),(18,'GN0429760','CH1486126118','20190131',10,652,'Buy'),(19,'GN0969461','DE7478496562','20190210',8,845,'Buy'),(20,'GN0969461','DE6592028664','20190213',2,981,'Buy'),(21,'GN0969461','DE9805831979','20190214',9,90,'Buy'),(22,'GN0369359','DE6592028664','20190227',7,518,'Buy'),(23,'GN0161162','GB9015779955','20190227',7,930,'Buy'),(24,'GN0429760','GB9015779955','20190312',8,301,'Buy'),(25,'GN0161162','GB9015779955','20190315',5,994,'Sell'),(26,'GN0969461','CH1486126118','20190329',8,928,'Buy'),(27,'GN0161162','DE7478496562','20190330',7,354,'Buy'),(28,'GN0369359','DE7478496562','20190423',1,928,'Buy'),(29,'GN0429760','DE6592028664','20190426',7,599,'Buy'),(30,'GN0161162','DE9805831979','20190428',5,283,'Buy'),(31,'GN0429760','DE9805831979','20190509',7,928,'Buy'),(32,'GN0429760','DE9805831979','20190510',9,954,'Buy'),(33,'GN0429760','CH1486126118','20190518',6,112,'Buy'),(34,'GN0369359','DE9805831979','20190520',8,304,'Buy'),(35,'GN0969461','DE6592028664','20190522',5,657,'Buy'),(36,'GN0161162','DE7478496562','20190526',5,127,'Buy'),(37,'GN0969461','CH1486126118','20190616',10,760,'Buy'),(38,'GN0429760','DE9805831979','20190618',10,659,'Buy'),(39,'GN0369359','DE7478496562','20190620',5,61,'Buy'),(40,'GN0369359','CH1486126118','20190704',5,50,'Buy'),(41,'GN0429760','DE9805831979','20190710',9,375,'Buy'),(42,'GN0969461','DE9805831979','20190722',5,999,'Buy'),(43,'GN0969461','DE6592028664','20190723',8,860,'Buy'),(44,'GN0161162','DE9805831979','20190723',8,80,'Buy'),(45,'GN0161162','GB9015779955','20190805',2,318,'Sell'),(46,'GN0429760','DE6592028664','20190809',6,505,'Buy'),(47,'GN0161162','GB9015779955','20190831',10,127,'Buy'),(48,'GN0429760','DE6592028664','20190831',7,9,'Buy'),(49,'GN0369359','CH1486126118','20190903',6,903,'Buy'),(50,'GN0429760','GB9015779955','20190913',6,430,'Buy'),(51,'GN0161162','DE6592028664','20190914',7,777,'Sell'),(52,'GN0429760','DE9805831979','20190915',5,372,'Buy'),(53,'GN0969461','DE6592028664','20190918',1,598,'Buy'),(54,'GN0429760','CH1486126118','20191018',2,894,'Buy'),(55,'GN0161162','CH1486126118','20191021',4,235,'Buy'),(56,'GN0969461','CH1486126118','20191031',1,427,'Buy'),(57,'GN0161162','DE9805831979','20191119',6,412,'Buy'),(58,'GN0969461','DE6592028664','20191205',2,769,'Sell'),(59,'GN0369359','DE9805831979','20191214',8,418,'Sell'),(60,'GN0429760','DE6592028664','20191219',6,420,'Buy'),(61,'GN0429760','DE7478496562','20191223',6,285,'Buy'),(62,'GN0429760','CH1486126118','20200101',5,106,'Buy'),(63,'GN0161162','GB9015779955','20200108',7,528,'Buy'),(64,'GN0429760','CH1486126118','20200116',7,531,'Buy'),(65,'GN0969461','DE6592028664','20200126',1,632,'Sell'),(66,'GN0429760','GB9015779955','20200129',6,21,'Buy'),(67,'GN0369359','GB9015779955','20200131',5,130,'Sell'),(68,'GN0161162','DE7478496562','20200212',10,222,'Sell'),(69,'GN0161162','CH1486126118','20200216',2,686,'Sell'),(70,'GN0969461','CH1486126118','20200219',10,546,'Buy'),(71,'GN0161162','CH1486126118','20200225',2,601,'Sell'),(72,'GN0369359','GB9015779955','20200305',4,722,'Buy'),(73,'GN0429760','CH1486126118','20200311',10,543,'Buy'),(74,'GN0369359','DE6592028664','20200314',4,145,'Buy'),(75,'GN0429760','DE6592028664','20200323',8,919,'Buy'),(76,'GN0429760','DE9805831979','20200401',1,4,'Buy'),(77,'GN0161162','CH1486126118','20200401',9,320,'Buy'),(78,'GN0429760','DE7478496562','20200406',2,46,'Buy'),(79,'GN0429760','CH1486126118','20200421',5,453,'Buy'),(80,'GN0969461','CH1486126118','20200425',8,150,'Buy'),(81,'GN0429760','DE7478496562','20200501',10,426,'Buy'),(82,'GN0369359','GB9015779955','20200511',4,514,'Buy'),(83,'GN0161162','DE6592028664','20200511',8,467,'Buy'),(84,'GN0969461','DE9805831979','20200518',1,517,'Buy'),(85,'GN0369359','DE6592028664','20200520',6,744,'Buy'),(86,'GN0161162','DE7478496562','20200602',7,567,'Sell'),(87,'GN0969461','CH1486126118','20200604',7,973,'Buy'),(88,'GN0369359','DE6592028664','20200605',2,39,'Sell'),(89,'GN0369359','GB9015779955','20200627',1,770,'Buy'),(90,'GN0369359','DE9805831979','20200709',3,918,'Buy'),(91,'GN0969461','CH1486126118','20200714',7,527,'Buy'),(92,'GN0161162','DE9805831979','20200725',9,281,'Buy'),(93,'GN0161162','GB9015779955','20200729',8,205,'Buy'),(94,'GN0969461','DE9805831979','20200730',5,682,'Buy'),(95,'GN0369359','GB9015779955','20200813',1,833,'Buy'),(96,'GN0161162','DE9805831979','20200816',9,914,'Sell'),(97,'GN0429760','DE7478496562','20200816',6,247,'Buy'),(98,'GN0969461','DE7478496562','20200828',8,822,'Buy'),(99,'GN0161162','DE6592028664','20200830',2,937,'Buy'),(100,'GN0161162','GB9015779955','20200905',5,858,'Buy');

DECLARE @isin nvarchar(12);
DECLARE @subaccount nvarchar(20);
SET @isin = 'GB9015779955';
SET @subaccount = 'GN0161162';


WITH UnitsCTE
AS
(
-- GET Total Units Left
    SELECT SUM(
            CASE buysell 
            WHEN 'Buy' Then quantity 
            When 'Sell' THEN -quantity 
            ELSE quantity END) AS Units
    FROM #CustomerOrderTable
    WHERE investor = @subaccount AND isin = @isin
), PurchaseCTE
AS
(
-- Get only purchases in reverse order
    SELECT quantity, price, datetime, ROW_NUMBER() OVER (ORDER BY datetime DESC ) AS RN
    FROM #CustomerOrderTable
    WHERE buysell <> 'Sale' AND investor = @subaccount AND isin = @isin
),
UnitCost
AS
(
-- Recursive CTE to get number of units left at each price
    SELECT CASE WHEN quantity > unitsCTE.Units THEN unitsCTE.Units ELSE quantity END As quantity, price
    FROM PurchaseCTE 
    CROSS APPLY unitsCTE
    WHERE RN = 1
    UNION ALL
    SELECT CASE WHEN P1.quantity > (unitsCTE.Units - (SELECT SUM(quantity) FROM PurchaseCTE P3 WHERE p3.RN < p1.RN))
            THEN    CASE WHEN (unitsCTE.Units - (SELECT SUM(quantity) FROM PurchaseCTE P3 WHERE p3.RN < p1.RN)) < 0 THEN 0
                        ELSE (unitsCTE.Units - (SELECT SUM(quantity) FROM PurchaseCTE P3 WHERE p3.RN < p1.RN)) END
            ELSE P1.quantity END,
            P1.price 
    FROM PurchaseCTE P1
    INNER JOIN PurchaseCTE P2
        ON P1.RN = P2.RN + 1
    CROSS APPLY unitsCTE
)
SELECT SUM(quantity) Units, SUM(price * quantity) / SUM(quantity) AS UnitCost, SUM(quantity * price) AS TotalCost
FROM UnitCost
WHERE quantity > 0


DROP TABLE #CustomerOrderTable

The current output from the query above is:

+-------+----------+-----------+
| Units | UnitCost | TotalCost |
+-------+----------+-----------+
|    30 | 363.2    |     10896 |
+-------+----------+-----------+

I would want the following output:

+-----------+--------------+-------+-------------+-----------+
| investor  |     ISIN     | Units |  UnitCost   | TotalCost |
+-----------+--------------+-------+-------------+-----------+
| GN0161162 | DE6592028664 |    11 | 580.6363636 |      6387 |
| GN0161162 | DE7478496562 |     2 | 567         |      1134 |
| GN0161162 | DE9805831979 |    22 | 563.7727273 |     12403 |
| GN0161162 | GB9015779955 |    30 | 363.2       |     10896 |
| GN0161162 | CH1486126118 |     9 | 320         |      2880 |
| GN0369359 | GB9015779955 |    12 | 567.25      |      6807 |
| GN0369359 | DE6592028664 |    15 | 445.0666667 |      6676 |
| GN0369359 | DE7478496562 |     6 | 205.5       |      1233 |
| GN0369359 | DE9805831979 |     3 | 918         |      2754 |
| GN0369359 | CH1486126118 |    11 | 515.2727273 |      5668 |
| GN0429760 | CH1486126118 |    58 | 495.5344828 |     28741 |
| GN0429760 | GB9015779955 |    35 | 506.2       |     17717 |
| GN0429760 | DE7478496562 |    30 | 355.0666667 |     10652 |
| GN0429760 | DE6592028664 |    34 | 504.6470588 |     17158 |
| GN0429760 | DE9805831979 |    41 | 656.3658537 |     26911 |
| GN0969461 | DE6592028664 |    22 | 719.1818182 |     15822 |
| GN0969461 | DE7478496562 |    33 | 705.7272727 |     23289 |
| GN0969461 | GB9015779955 |    10 | 262         |      2620 |
| GN0969461 | DE9805831979 |    20 | 486.6       |      9732 |
| GN0969461 | CH1486126118 |    51 | 639.4313725 |     32611 |
+-----------+--------------+-------+-------------+-----------+

回答1:


I will answer my own question, because I have made it work. I hope it will help people in the future:

CREATE TABLE #CustomerOrderTable (
    ID INTEGER NOT NULL,
    [investor] VARCHAR(255) NOT NULL,
    [isin] VARCHAR(255) NOT NULL,
    [datetime] DATETIME NOT NULL,
    [quantity] FLOAT NOT NULL,
    [price] FLOAT NOT NULL,
    [buysell] VARCHAR(255) NOT NULL,
    PRIMARY KEY (ID)
);
GO

INSERT INTO #CustomerOrderTable(ID, [investor],[isin],[datetime],[quantity],[price],[buysell]) VALUES (1,'GN0161162','DE6592028664','20180913',7,88,'Buy'),(2,'GN0969461','DE6592028664','20180924',4,452,'Buy'),(3,'GN0969461','DE7478496562','20180929',9,721,'Buy'),(4,'GN0429760','CH1486126118','20180929',9,451,'Buy'),(5,'GN0429760','GB9015779955','20181006',9,817,'Buy'),(6,'GN0969461','DE7478496562','20181013',8,433,'Buy'),(7,'GN0969461','GB9015779955','20181025',10,262,'Buy'),(8,'GN0161162','DE7478496562','20181108',7,278,'Buy'),(9,'GN0429760','GB9015779955','20181109',3,986,'Buy'),(10,'GN0161162','DE6592028664','20181114',1,556,'Buy'),(11,'GN0429760','DE7478496562','20181120',4,434,'Buy'),(12,'GN0429760','GB9015779955','20181121',3,764,'Buy'),(13,'GN0369359','GB9015779955','20181123',7,129,'Buy'),(14,'GN0429760','DE7478496562','20181205',2,686,'Buy'),(15,'GN0969461','DE6592028664','20181231',5,309,'Buy'),(16,'GN0161162','DE9805831979','20190108',3,540,'Buy'),(17,'GN0429760','CH1486126118','20190128',4,940,'Buy'),(18,'GN0429760','CH1486126118','20190131',10,652,'Buy'),(19,'GN0969461','DE7478496562','20190210',8,845,'Buy'),(20,'GN0969461','DE6592028664','20190213',2,981,'Buy'),(21,'GN0969461','DE9805831979','20190214',9,90,'Buy'),(22,'GN0369359','DE6592028664','20190227',7,518,'Buy'),(23,'GN0161162','GB9015779955','20190227',7,930,'Buy'),(24,'GN0429760','GB9015779955','20190312',8,301,'Buy'),(25,'GN0161162','GB9015779955','20190315',5,994,'Sell'),(26,'GN0969461','CH1486126118','20190329',8,928,'Buy'),(27,'GN0161162','DE7478496562','20190330',7,354,'Buy'),(28,'GN0369359','DE7478496562','20190423',1,928,'Buy'),(29,'GN0429760','DE6592028664','20190426',7,599,'Buy'),(30,'GN0161162','DE9805831979','20190428',5,283,'Buy'),(31,'GN0429760','DE9805831979','20190509',7,928,'Buy'),(32,'GN0429760','DE9805831979','20190510',9,954,'Buy'),(33,'GN0429760','CH1486126118','20190518',6,112,'Buy'),(34,'GN0369359','DE9805831979','20190520',8,304,'Buy'),(35,'GN0969461','DE6592028664','20190522',5,657,'Buy'),(36,'GN0161162','DE7478496562','20190526',5,127,'Buy'),(37,'GN0969461','CH1486126118','20190616',10,760,'Buy'),(38,'GN0429760','DE9805831979','20190618',10,659,'Buy'),(39,'GN0369359','DE7478496562','20190620',5,61,'Buy'),(40,'GN0369359','CH1486126118','20190704',5,50,'Buy'),(41,'GN0429760','DE9805831979','20190710',9,375,'Buy'),(42,'GN0969461','DE9805831979','20190722',5,999,'Buy'),(43,'GN0969461','DE6592028664','20190723',8,860,'Buy'),(44,'GN0161162','DE9805831979','20190723',8,80,'Buy'),(45,'GN0161162','GB9015779955','20190805',2,318,'Sell'),(46,'GN0429760','DE6592028664','20190809',6,505,'Buy'),(47,'GN0161162','GB9015779955','20190831',10,127,'Buy'),(48,'GN0429760','DE6592028664','20190831',7,9,'Buy'),(49,'GN0369359','CH1486126118','20190903',6,903,'Buy'),(50,'GN0429760','GB9015779955','20190913',6,430,'Buy'),(51,'GN0161162','DE6592028664','20190914',7,777,'Sell'),(52,'GN0429760','DE9805831979','20190915',5,372,'Buy'),(53,'GN0969461','DE6592028664','20190918',1,598,'Buy'),(54,'GN0429760','CH1486126118','20191018',2,894,'Buy'),(55,'GN0161162','CH1486126118','20191021',4,235,'Buy'),(56,'GN0969461','CH1486126118','20191031',1,427,'Buy'),(57,'GN0161162','DE9805831979','20191119',6,412,'Buy'),(58,'GN0969461','DE6592028664','20191205',2,769,'Sell'),(59,'GN0369359','DE9805831979','20191214',8,418,'Sell'),(60,'GN0429760','DE6592028664','20191219',6,420,'Buy'),(61,'GN0429760','DE7478496562','20191223',6,285,'Buy'),(62,'GN0429760','CH1486126118','20200101',5,106,'Buy'),(63,'GN0161162','GB9015779955','20200108',7,528,'Buy'),(64,'GN0429760','CH1486126118','20200116',7,531,'Buy'),(65,'GN0969461','DE6592028664','20200126',1,632,'Sell'),(66,'GN0429760','GB9015779955','20200129',6,21,'Buy'),(67,'GN0369359','GB9015779955','20200131',5,130,'Sell'),(68,'GN0161162','DE7478496562','20200212',10,222,'Sell'),(69,'GN0161162','CH1486126118','20200216',2,686,'Sell'),(70,'GN0969461','CH1486126118','20200219',10,546,'Buy'),(71,'GN0161162','CH1486126118','20200225',2,601,'Sell'),(72,'GN0369359','GB9015779955','20200305',4,722,'Buy'),(73,'GN0429760','CH1486126118','20200311',10,543,'Buy'),(74,'GN0369359','DE6592028664','20200314',4,145,'Buy'),(75,'GN0429760','DE6592028664','20200323',8,919,'Buy'),(76,'GN0429760','DE9805831979','20200401',1,4,'Buy'),(77,'GN0161162','CH1486126118','20200401',9,320,'Buy'),(78,'GN0429760','DE7478496562','20200406',2,46,'Buy'),(79,'GN0429760','CH1486126118','20200421',5,453,'Buy'),(80,'GN0969461','CH1486126118','20200425',8,150,'Buy'),(81,'GN0429760','DE7478496562','20200501',10,426,'Buy'),(82,'GN0369359','GB9015779955','20200511',4,514,'Buy'),(83,'GN0161162','DE6592028664','20200511',8,467,'Buy'),(84,'GN0969461','DE9805831979','20200518',1,517,'Buy'),(85,'GN0369359','DE6592028664','20200520',6,744,'Buy'),(86,'GN0161162','DE7478496562','20200602',7,567,'Sell'),(87,'GN0969461','CH1486126118','20200604',7,973,'Buy'),(88,'GN0369359','DE6592028664','20200605',2,39,'Sell'),(89,'GN0369359','GB9015779955','20200627',1,770,'Buy'),(90,'GN0369359','DE9805831979','20200709',3,918,'Buy'),(91,'GN0969461','CH1486126118','20200714',7,527,'Buy'),(92,'GN0161162','DE9805831979','20200725',9,281,'Buy'),(93,'GN0161162','GB9015779955','20200729',8,205,'Buy'),(94,'GN0969461','DE9805831979','20200730',5,682,'Buy'),(95,'GN0369359','GB9015779955','20200813',1,833,'Buy'),(96,'GN0161162','DE9805831979','20200816',9,914,'Sell'),(97,'GN0429760','DE7478496562','20200816',6,247,'Buy'),(98,'GN0969461','DE7478496562','20200828',8,822,'Buy'),(99,'GN0161162','DE6592028664','20200830',2,937,'Buy'),(100,'GN0161162','GB9015779955','20200905',5,858,'Buy');


;WITH UnitsCTE
AS
(
-- GET Total Units Left
    SELECT investor, isin, SUM(
            CASE buysell 
            WHEN 'Buy' Then quantity 
            When 'Sell' THEN -quantity 
            ELSE quantity END) AS Units
    FROM #CustomerOrderTable
    GROUP BY investor, isin
), PurchaseCTE
AS
(
-- Get only purchases in reverse order
    SELECT quantity, price, investor, isin, datetime, ROW_NUMBER() OVER (PARTITION BY investor, isin ORDER BY datetime DESC ) AS RN
    FROM #CustomerOrderTable
    WHERE buysell <> 'Sale'
),
UnitCost
AS
(
-- Recursive CTE to get number of units left at each price
    SELECT CASE WHEN quantity > unitsCTE.Units THEN unitsCTE.Units ELSE quantity END As quantity, price, unitsCTE.investor, unitsCTE.isin
    FROM PurchaseCTE 
    CROSS APPLY unitsCTE
    WHERE RN = 1
    AND unitsCTE.investor = PurchaseCTE.investor AND unitsCTE.isin = PurchaseCTE.isin
    UNION ALL
    SELECT CASE WHEN P1.quantity > (unitsCTE.Units - (SELECT SUM(quantity) FROM PurchaseCTE P3 WHERE p3.RN < p1.RN AND P1.investor = P3.investor AND P1.isin = P3.isin))
            THEN    CASE WHEN (unitsCTE.Units - (SELECT SUM(quantity) FROM PurchaseCTE P3 WHERE p3.RN < p1.RN AND P1.investor = P3.investor AND P1.isin = P3.isin)) < 0 THEN 0
                        ELSE (unitsCTE.Units - (SELECT SUM(quantity) FROM PurchaseCTE P3 WHERE p3.RN < p1.RN AND P1.investor = P3.investor AND P1.isin = P3.isin)) END
            ELSE P1.quantity END,
            P1.price, P1.investor, P1.isin 
    FROM PurchaseCTE P1
    INNER JOIN PurchaseCTE P2
        ON P1.RN = P2.RN + 1 AND P1.investor = P2.investor AND P1.isin = P2.isin
    CROSS APPLY unitsCTE
    WHERE P1.investor = unitsCTE.investor AND P1.isin = unitsCTE.isin
)

SELECT investor, isin, SUM(quantity) Units, SUM(price * quantity) / SUM(quantity) AS UnitCost, SUM(quantity * price) AS TotalCost
FROM UnitCost
WHERE quantity > 0
GROUP BY investor, isin

DROP TABLE #CustomerOrderTable


来源:https://stackoverflow.com/questions/57901906/fifo-closing-price-with-multiple-products-and-people

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!