Рассчитать средневзвешенную цену покупки (таблица сделок с возвратом) - PullRequest
0 голосов
/ 11 января 2019

Я пытаюсь рассчитать средневзвешенную цену акций, используя Microsoft SQL Server 2016. Разница в том, что я спрашиваю, состоит в том, что, когда все акции были проданы, взвешенная цена должна быть сброшена

Выпуск
NewPrice неверен в следующей таблице сделок

enter image description here

Ожидаемый результат

Row   NewPrice
1      186.4000
2      186.4000
3      183.0800
4      183.0800
5      183.0800
6      183.0800
7      183.0800
8      183.0800
7      183.0800

Запрос

SELECT  *,   
                        PriceRunningTotalFinal = 
                        SUM(CASE 
                                WHEN QuantityRunningTotal = 0 THEN -1 * PriceRunningTotal
                                WHEN Units < 0 THEN 0 ELSE PurchasePrice * Units END) OVER 
                        (
                        PARTITION BY UserCompetitionId, StockId
                        ORDER BY Id ROWS UNBOUNDED PRECEDING
                        ),   


                        CASE WHEN Results1.QuantityRunningTotal <= 0 then 0
                        else 
                        SUM(CASE 
                                WHEN QuantityRunningTotal <= 0 THEN -1 * PriceRunningTotal
                                WHEN Units < 0 THEN 0 ELSE PurchasePrice * Units END) OVER 
                        (
                        PARTITION BY UserCompetitionId, StockId
                        ORDER BY Id ROWS UNBOUNDED PRECEDING
                        )   / Results1.QuantityRunningTotal 
                        end as NewPrice
                    FROM 
                        (
                        SELECT Id, UserCompetitionId, StockId, Type,  Units, PurchasePrice, CreatedOn,
                          QuantityRunningTotal = SUM(Units) OVER 
                          (
                            PARTITION BY UserCompetitionId, StockId
                            ORDER BY Id ROWS UNBOUNDED PRECEDING
                          ),  
                          PriceRunningTotal = SUM(CASE WHEN Units <= 0 THEN 0 ELSE PurchasePrice * Units END) OVER 
                          (
                            PARTITION BY UserCompetitionId, StockId
                            ORDER BY Id ROWS UNBOUNDED PRECEDING
                          ) 

                          FROM Trade
                        ) AS Results1
                        WHERE UserCompetitionId =@UserCompetitionId AND StockId = 122

У меня проблема с новой ценой.
С Строка 4 и далее цена должна быть 183.08000

Это связано с тем, что все предыдущие акции были проданы (количествоRunningTotal равно 0). Поэтому расчет взвешенной цены покупки должен начаться заново, а не принимать во внимание предыдущие строки

UPDATE
Глядя на Андрей Одегов ответ, решение работает. Но я не упомянул одну вещь - я хотел бы получить скорректированные взвешенные цены для каждой строки. Как видно на этом скриншоте: enter image description here

Средневзвешенная цена Я пытался получить среднюю цену, она вроде работает, но я не уверен, что это правильный путь?

DECLARE @Trade TABLE(
  Id INT IDENTITY,
  UserCompetitionId INT DEFAULT(92170),
  StockId INT DEFAULT(122),
  Type INT,
  Units INT,
  PurchasePrice NUMERIC(8, 4)
);

INSERT @Trade(Type, Units, PurchasePrice)
  VALUES (10, 42, 186.4),
         (20, -42, 183.08),
         (10, 40, 183.08),
         (20, -5, 183.92),
         (20, -1, 181.68),
         (20, -1, 181.68),
         (20, -1, 181.68),         
         (20, 17, 181.68),
         (20, -10, 181.68);

WITH
  A AS(
    SELECT
      *,
      QuantityRunningTotal = SUM(Units) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      )
    FROM @Trade
  ),
  B AS (
    SELECT
      *,
      Grp = SUM(IIF(QuantityRunningTotal - Units <= 0, 1, 0)) OVER
            (
              PARTITION BY UserCompetitionId, StockId
              ORDER BY Id ROWS UNBOUNDED PRECEDING
            )
    FROM A
  )


  SELECT *, TotalAmount / Qty
  FROM (

    SELECT
      *,
      Units * PurchasePrice AS PurchaseAmount,
      NewPrice = FIRST_VALUE(PurchasePrice) OVER
      (
        PARTITION BY UserCompetitionId, StockId, Grp
        ORDER BY Id ROWS UNBOUNDED PRECEDING
      ),
      row_number() over (partition by UserCompetitionId, StockId order by Id desc) as Seq,
       TotalAmount = SUM(CASE WHEN Units < 0 THEN 0 ELSe Units * PurchasePrice END) OVER
      (
        PARTITION BY UserCompetitionId, StockId, Grp
        ORDER BY Id ROWS UNBOUNDED PRECEDING
      ),
       Qty = SUM(CASE WHEN Units < 0 THEN 0 ELSe Units  END) OVER
      (
        PARTITION BY UserCompetitionId, StockId, Grp
        ORDER BY Id ROWS UNBOUNDED PRECEDING
      )
    FROM B
    )AS Result
WHERE Result.Seq = 1 AND QuantityRunningTotal > 0
ORDER BY StockId;

UPDATE2 (Получает общую стоимость покупки $ 50,65)

DECLARE @Trade TABLE(
  Id INT IDENTITY,
  UserCompetitionId INT DEFAULT(92170),
  StockId INT DEFAULT(122),
  Units INT,
  PurchasePrice NUMERIC(8, 4)
);

INSERT @Trade(Units, PurchasePrice)
VALUES (100, 97.8774),
       (200, 97.89),
       (-300, 97.8858),
       (600, 48.9429),
       (100, 60.9),
       (-600, 60.395);

WITH
  A AS(
    SELECT
      *,
      Amount = SUM(IIF(Units > 0, Units * PurchasePrice, 0)) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      ),
      Qty = SUM(IIF(Units > 0, Units, 0)) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      ),
      Qty2 = SUM(Units) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      ),

      TotalAmount = SUM(Units * PurchasePrice) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      )
     FROM @Trade            
  )
SELECT
  Units AS Amount,
  CAST(PurchasePrice AS NUMERIC(6,2)) AS [Purchase Price],
  CAST(ABS(Units * PurchasePrice) AS NUMERIC(12,2)) AS [Purchase Amount],
  CAST(Amount / Qty AS NUMERIC(6, 2)) AS "Adjusted Purchase Price",
  Qty2,
  TotalAmount,
  IIF(Qty2 = 0, 0,TotalAmount/ Qty2) AS Average
FROM A
ORDER BY Id;

Любые идеи будут с благодарностью.

1 Ответ

0 голосов
/ 11 января 2019

Вы можете получить желаемый результат, используя функцию FIRST_VALUE.

WITH
  A AS(
    SELECT
      *,
      QuantityRunningTotal = SUM(Units) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      )
    FROM @Trade
  ),
  B AS (
    SELECT
      *,
      Grp = SUM(IIF(QuantityRunningTotal - Units <= 0, 1, 0)) OVER
            (
              PARTITION BY UserCompetitionId, StockId
              ORDER BY Id ROWS UNBOUNDED PRECEDING
            )
    FROM A
  )
SELECT
  *,
  NewPrice = FIRST_VALUE(PurchasePrice) OVER
  (
    PARTITION BY UserCompetitionId, StockId, Grp
    ORDER BY Id ROWS UNBOUNDED PRECEDING
  )
FROM B
ORDER BY Id;

Выход:

+----+-------------------+---------+------+-------+---------------+----------------------+-----+----------+
| Id | UserCompetitionId | StockId | Type | Units | PurchasePrice | QuantityRunningTotal | Grp | NewPrice |
+----+-------------------+---------+------+-------+---------------+----------------------+-----+----------+
|  1 |             92170 |     122 |   10 |    42 | 186,4000      |                   42 |   1 | 186,4000 |
|  2 |             92170 |     122 |   20 |   -42 | 183,0800      |                    0 |   1 | 186,4000 |
|  3 |             92170 |     122 |   10 |    40 | 183,0800      |                   40 |   2 | 183,0800 |
|  4 |             92170 |     122 |   20 |    -5 | 183,9200      |                   35 |   2 | 183,0800 |
|  5 |             92170 |     122 |   20 |    -1 | 181,6800      |                   34 |   2 | 183,0800 |
|  6 |             92170 |     122 |   20 |    -1 | 181,6800      |                   33 |   2 | 183,0800 |
|  7 |             92170 |     122 |   20 |    -1 | 181,6800      |                   32 |   2 | 183,0800 |
|  8 |             92170 |     122 |   20 |    -2 | 181,6800      |                   30 |   2 | 183,0800 |
|  9 |             92170 |     122 |   20 |   -30 | 181,6800      |                    0 |   2 | 183,0800 |
| 10 |             92170 |     122 |   20 |    17 | 181,6800      |                   17 |   3 | 181,6800 |
| 11 |             92170 |     122 |   20 |   -10 | 181,6800      |                    7 |   3 | 181,6800 |
+----+-------------------+---------+------+-------+---------------+----------------------+-----+----------+

Демо-версия:
https://rextester.com/IMN29774.

Обновление:
Цифры этого скриншота можно получить с помощью следующего запроса:

DECLARE @Trade TABLE(
  Id INT IDENTITY,
  UserCompetitionId INT DEFAULT(92170),
  StockId INT DEFAULT(122),
  Units INT,
  PurchasePrice NUMERIC(8, 4)
);

INSERT @Trade(Units, PurchasePrice)
VALUES (100, 97.8774),
       (200, 97.89),
       (-300, 97.8858),
       (600, 48.9429),
       (100, 60.9),
       (-600, 60.395);

WITH
  A AS(
    SELECT
      *,
      Amount = SUM(IIF(Units > 0, Units * PurchasePrice, 0)) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      ),
      Qty = SUM(IIF(Units > 0, Units, 0)) OVER
      (
       PARTITION BY UserCompetitionId, StockId
       ORDER BY Id ROWS UNBOUNDED PRECEDING
      )
    FROM @Trade
  )
SELECT
  Units AS Amount,
  CAST(PurchasePrice AS NUMERIC(6,2)) AS [Purchase Price],
  CAST(ABS(Units * PurchasePrice) AS NUMERIC(12,2)) AS [Purchase Amount],
  CAST(Amount / Qty AS NUMERIC(6, 2)) AS "Adjusted Purchase Price"
FROM A
ORDER BY Id;

Выход:

+--------+--------------+---------------+-----------------------+
| Amount |Purchase Price|Purchase Amount|Adjusted Purchase Price|
+--------+--------------+---------------+-----------------------+
|    100 | 97,88        | 9787,74       | 97,88                 |
|    200 | 97,89        | 19578,00      | 97,89                 |
|   -300 | 97,89        | 29365,74      | 97,89                 |
|    600 | 48,94        | 29365,74      | 65,26                 |
|    100 | 60,90        | 6090,00       | 64,82                 |
|   -600 | 60,40        | 36237,00      | 64,82                 |
+--------+--------------+---------------+-----------------------+
...