【问题标题】:Stocks wallet with running values and average buying prices with reset具有运行价值的股票钱包和重置的平均购买价格
【发布时间】:2020-08-23 22:45:43
【问题描述】:

我正在处理个人投资工作表,并考虑使其有用并能够计算每个资产余额和当前平均值所需的最少信息量。

我看到了几个示例,其中大多数依赖于借方和贷方列,或者使用单行来添加买入/卖出价格;第一种情况需要至少两行来表示钱的去向;第二个很难表达部分卖出(比如买 100 只股票,卖出 50 只,然后再买 50 只;当前头寸和平均买入价值是多少?)

考虑到这一点,我提出了类似I described here

考虑以下数据集:

SELECT  *
FROM    (
VALUES  (1, '2020-08-01', 'MBIT', 'POCKET', 'MONEY', 100.00, 100.00, 'Added $100 from my pocket into MBIT broker'),
        (2, '2020-08-02', 'MBIT', 'MONEY' , 'XPTO' ,   0.50, 100.00, 'Bought 0.50 XPTO for $100 (thus rate=200)'),
        (3, '2020-08-03', 'MBIT', 'XPTO'  , 'MONEY',   0.50, 125.00, 'Sold   0.50 XPTO for $125 (thus rate=250, $25 profit)'),
        (4, '2020-08-04', 'MBIT', 'MONEY' , 'XPTO' ,   0.35,  85.00, 'Bought 0.35 XPTO for $85  (thus rate=~242.85)'),
        (5, '2020-08-05', 'MBIT', 'MONEY' , 'WXYZ' ,   1.75,  20.00, 'Bought 1.75 WXYZ for $20 (rate~=11.43)'),
        (6, '2020-08-06', 'MBIT', 'MONEY' , 'WXYZ' ,   1.85,  15.00, 'Bought 1.85 WXYZ for $15 (new total = 3.6 for $35, rate~=9.72)'))
        Entries([Order#], Date, Broker,  Debit, Credit, Quantity, Value, Description)

我想计算每个经纪人/资产的当前平均值和余额;该怎么做?

【问题讨论】:

标签: sql-server accounting


【解决方案1】:

第一步是确定应该被评估为货币的资产(即固定利率为 1)并存储其他资产的当前报价:

WITH    Coins   AS
(       SELECT  *
        FROM    (
        VALUES  ('POCKET', 1,   1.00),  -- Pocket and money always have $1 price
                ('MONEY' , 1,   1.00),  -- Pocket and money always have $1 price
                ('XPTO'  , 0, 200.00),  -- Current rate for this asset
                ('WXYZ'  , 0,   8.50))
                Entries(Name, IsMoney, [Current])
)

然后我们需要从给定的数据集(命名为RawData)中拆分每一行,这样我们就可以有一行用于贷方部分,另一行用于借方部分。我们还需要计算每种非货币资产的费率:

,       Accounting  AS
(       SELECT  ROW_NUMBER() OVER (ORDER BY Date, Order#, Quantity) Sequence,
                [Order#], Date, Broker, Account, IsMoney, Description,
                CASE WHEN Coins.IsMoney = 1 THEN Value ELSE Quantity END Quantity, Value /
                CASE WHEN Coins.IsMoney = 1 THEN Value ELSE Quantity END Rate
        FROM    (   SELECT  [Order#], Date, Broker, Debit  AS Account,
                            -Quantity Quantity, -Value Value, Description
                    FROM    RawData
                    UNION   ALL
                    SELECT  [Order#], Date, Broker, Credit AS Account,
                            +Quantity Quantity, +Value Value, Description
                    FROM    RawData
                )   Accounting
        LEFT
        JOIN    Coins
            ON  Coins.Name = Accounting.Account
)

非常简单;到目前为止,我们得到了这个:

SuperUser question comments 上,我发现计算平均价格会很棘手,特别是考虑到我需要忽略已平仓头寸。

由于出售资产余额的操作与购买它的一个/多个没有直接关系,所以我已经死在水里了。

转折点是当我意识到我不需要匹配那些时:我所需要的只是找出余额何时归零,并且只使用它的最新“版本”。

所以我转向窗口函数;这里的挑战是确定余额何时重置。一些谷歌搜索把我带到了T-SQL Feature Request: Add RESET WHEN Clause to Reset Window Partition,作者精彩地描述了请求功能如何帮助进行更简单的查询,并给出了如何在微软人员点赞的同时克服它们的提示。

,   Balance AS
(   SELECT  *,
            MAX(BalanceVersion) OVER (PARTITION BY Broker, Account) LatestVersion
    FROM    (   SELECT  *,
                SUM(BalanceReset) OVER (
                    PARTITION BY Broker, Account
                    ORDER BY Sequence)  BalanceVersion
                FROM    (   SELECT  *,
                                    CASE    WHEN    LAG(Total) OVER (
                                                        PARTITION BY Broker, Account
                                                        ORDER BY Sequence) = 0
                                            THEN    1
                                            ELSE    0
                                    END     BalanceReset
                            FROM    (   SELECT  *,
                                                SUM (Quantity) OVER (
                                                    PARTITION BY Broker, Account
                                                    ORDER BY Sequence) AS Total
                                        FROM    Accounting
                                    )   Account
                        )   Reset
            )   Versions
)

拆开这个东西:

  • 让我们首先计算每个资产的总和 (Account subselect)
  • 我们使用LAG functionReset 子选择)识别余额何时变为零
  • 让我们计算一个新的运行总计,现在使用重置标识符对余额进行分组 (Versions subselect)
  • 最后一步是确定每个经纪人/资产组的最新版本

通过识别“数据孤岛”,我们又回到了游戏中!

现在让我们计算未平仓头寸的正确平均值和最终余额:

,   Final AS
(   SELECT  Broker, Account,
            SUM(Quantity) Quantity,
            SUM(CASE WHEN IsMoney = 1 THEN 1.000 ELSE [Rate] * Quantity  END) /
            SUM(CASE WHEN IsMoney = 1 THEN 1.000 ELSE Quantity  END) Rate
    FROM    Balance
    WHERE   BalanceVersion = LatestVersion
    GROUP
        BY  Broker, Account
)

构建正确的XPTO平衡需要所有的窗口函数疯狂;对于该计算,我们不能使用第一次买入/卖出操作,因为它会将头寸归零,我们需要重新开始以获得正确的平均买入价。

查询的其余部分只是将当前资产价格以及计算出的余额和平均价格放在一起:

SELECT  Final.Broker,
        Final.Account,
        Final.Quantity,
        Final.Rate  [Original],
         Coins.[Current]    [Current],
        (Coins.[Current] / Final.Rate)-1 [%],
        (Coins.[Current] * Final.Quantity) [Value]
FROM    Final
LEFT
JOIN    Coins
    ON  Coins.Name = Final.Account
ORDER
    BY  Final.Broker,
        Final.Account

结果是这样的:

查看我们可以看到的数据:

  • 我们在该经纪人身上投资了多少(POCKET 价值)
  • 我们在经纪人中闲置了多少钱(MONEY 值)
  • WXYZ的余额
    • 我们首先以 20 美元的价格购买了 1.75 WXYZ(汇率:20/1.75 ~=11.43)
    • 然后我们以 15 美元购买了更多 1.85 WXYZ(汇率:15/1.85 ~=8.10)
    • 因此正确的平均价格为 (20+15)/(1.75+1.85) ~= 9.72
  • XPTO的余额
    • 如果我们考虑所有操作,我们的平均值将为 171.43
    • 这是错误的,因为如果我们购买了一些资产数量并出售了所有资产,那么这些价格不应影响新的购买价格
    • 因此,该资产的正确平均值约为 242.85

结论:在这个假设场景中:

  • 我们投资了 100 美元;
  • 获利 25 美元;
  • 买了一些 XPTO
  • 买了一些WXYZ
  • WXYZ 市场价格下跌,我们又买了一些
  • XPTO 自我们上次购买以来下跌了 12.57%
  • 考虑到之前两次收购的平均价格,WXYZ 下跌了 17.65%
  • 如果我们将最后一列中的所有值相加,我们可以看到我们仍有 5.60 美元的利润

【讨论】:

    猜你喜欢
    • 2011-03-15
    • 1970-01-01
    • 2020-10-03
    • 1970-01-01
    • 1970-01-01
    • 2019-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多